d41a45ba213a2cbd755410665b568e7466a14eb5
[core.git] / contrib / chash / chash.php
1 <?php
2 error_reporting(E_ALL | E_STRICT);
3
4 define('HASH_ALGO', MHASH_RIPEMD320);
5 define('BLOCK_SIZE', 1000);
6 define('NONCE_INCREMENT', 1.0);
7 define('START_TIME', microtime(TRUE));
8
9 /**
10  * Continued-hashing
11  *
12  * @author              Roland Haeder <roland@mxchange.org>
13  * @copyright   Copyright (c) 2013 by Core Developer Team
14  * @license             See LICENSE (public-domain)
15  */
16
17 /**
18  * Calculates a simple but stronger hash from given string. No salts are being
19  * added here.
20  *
21  * @param       $str    The string to be hashed
22  * @return      $hash   The hash from string $str
23  */
24 function hashString ($str) {
25         // Calculate strong hash from given string
26         $hash = mhash(HASH_ALGO, $str);
27
28         // Return it hexadecimal-encoded
29         return bin2hex($hash);
30 }
31
32 /**
33  * Double-hashes given string. This is done by hashing the given string and
34  * then hashing the generated hash again.
35  *
36  * @param       $str    The string to be hashed 4 times
37  * @return      $hash   The generated hash
38  */
39 function doubleHashString ($str) {
40         // Generate hash from given hash
41         $hash = hashString(hashString($str));
42
43         // Return it
44         return $hash;
45 }
46
47 /**
48  * Calculates a "modula-hash" based given two hashes.
49  *
50  * @param       $hash1  Hash 1
51  * @param       $hash2  Hash 2
52  */
53 function modulaHash ($hash1, $hash2) {
54         // Both must have same length
55         assert(strlen($hash1) === strlen($hash2));
56
57         // Init new hash
58         $modulaHash = '';
59
60         // "Walk" trough first hash and get every 2 byte of both hashes
61         for ($idx = 0; $idx < strlen($hash1); $idx += 2) {
62                 // Init modula value
63                 $mod = 0;
64
65                 // Get both hash parts and convert to ASCII number
66                 $part1 = hexdec(substr($hash1, $idx, 2));
67                 $part2 = hexdec(substr($hash2, $idx, 2));
68
69                 /*
70                  * If part1 is larget part2, part1 is divident and vise-versa. But don't do it
71                  * if one is zero
72                  */
73                 if (($part1 > $part2) && ($part2 > 0)) {
74                         // 'part1' is larger than 'part2'
75                         $mod = $part1 % $part2;
76                 } elseif (($part1 < $part2) && ($part1 > 0)) {
77                         // 'part2' is larger than 'part1'
78                         $mod = $part2 % $part1;
79                 }
80
81                 // "Invert" the result against 255
82                 $mod = 255 - $mod;
83
84                 // Encode to hex, pre-pad it with zeros and add to new hash
85                 $modulaHash .= padHex($mod);
86         } // END - for
87
88         // Modula hash must have same length as input hash
89         assert(strlen($modulaHash) === strlen($hash1));
90
91         // Return modula hash
92         return $modulaHash;
93 }
94
95 /**
96  * Calculates a "sqrt-hash" based given two hashes and single-hash it
97  *
98  * @param       $hash1  Hash 1
99  * @param       $hash2  Hash 2
100  */
101 function sqrtHash ($hash1, $hash2) {
102         // Both must have same length
103         assert(strlen($hash1) === strlen($hash2));
104
105         // Init new hash
106         $sqrtHash = '';
107
108         // "Walk" trough first hash and get every 2 byte of both hashes
109         for ($idx = 0; $idx < strlen($hash1); $idx += 2) {
110                 // Init modula value
111                 $mod = 0;
112
113                 // Get both hash parts and convert to ASCII number
114                 $part1 = hexdec(substr($hash1, $idx, 2));
115                 $part2 = hexdec(substr($hash2, $idx, 2));
116
117                 // Calculate square root of both parts being multiplied and round up, then "invert" it against 255
118                 $sqrt = intval(255 - ceil(sqrt($part1 * $part2)));
119
120                 // Encode to hex, pre-pad it with zeros and add to new hash
121                 $sqrtHash .= padHex($sqrt);
122         } // END - for
123
124         // "sqrt-hash" must have same length as input hash
125         assert(strlen($sqrtHash) === strlen($hash1));
126
127         // Hash reversed "sqrt-hash" again and return it
128         return hashString(strrev($sqrtHash));
129 }
130
131 /**
132  * Converts a number between 0 and 255 into a zero-padded hexadecimal string
133  *
134  * @param       $num    Number between 0 and 255
135  * @return      $hex    Hexadecimal string, padded with zeros
136  */
137 function padHex ($num) {
138         // Must be a integer number and between 0 and 255
139         assert(is_int($num));
140         assert($num >= 0);
141         assert($num <= 255);
142
143         // Convert it
144         $hex = str_pad(dechex($num), 2, '0', STR_PAD_LEFT);
145
146         // ... and return it
147         return $hex;
148 }
149
150 /**
151  * Calculates sum from given hash
152  *
153  * @param       $hash   Hash to calculate sum from
154  * @return      $sum    Sum from given hash
155  */
156 function calculateSumFromHash ($hash) {
157         // Everything starts with zero ...
158         $sum = 0;
159
160         // Loop through hash
161         for ($idx = 0; $idx < (strlen($hash) / 2); $idx++) {
162                 // And add it
163                 $sum = $sum + (hexdec(substr($hash, $idx, 2)) * $idx & 256);
164         } // END - for
165
166         // And return it
167         return $sum;
168 }
169
170 /**
171  * Calculates new nonce
172  *
173  * @param       $nonce          Old nonce to be used
174  * @return      $newNonce       New nonce
175  */
176 function calculateNonce ($nonce) {
177         // Linear incrementation
178         $newNonce = $nonce + NONCE_INCREMENT;
179
180         // Return new value
181         return $newNonce;
182 }
183
184 /**
185  * Calculates current block
186  *
187  * @return      $blockSize      Block size
188  * @todo        This needs improvement
189  */
190 function getBlockSize () {
191         // For now only a constant block size
192         return BLOCK_SIZE;
193 }
194
195 /*
196  * Calculate "genesis" hashes, please note that these "genesis strings" are now
197  * known to the public as you can read them here in source code and therefore I
198  * will not use them for the real genesis hashes.
199  */
200 $hashes = array(
201         // A famous quote from Deus Ex 2 - Invisible War
202         doublehashString('"Informations must be free." - AI Helios from Deus Ex'),
203         // My name + URL of my first StatusNet instance
204         doubleHashString('Roland Haeder, https://status.mxchange.org'),
205         // A famous quote from Linus Torwalds
206         doubleHashString('"Software is like sex. Its better when its free." - Linus Torwalds'),
207         // Possible truth ;-)
208         doubleHashString('September 11 is a big lie.'),
209
210         // GNU is not Uni*
211         doubleHashString('GNU is Not Uni*.'),
212         // WINE is not an emulator
213         doubleHashString('WINE Is Not an Emulator.'),
214         // FlightGear - Fly free!
215         doubleHashString('FlightGear - Fly free!'),
216         // Linus Torwalds Quote
217         doubleHashString('Your code is shit.. your argument is shit.'),
218 );
219
220 // Calculate "modula hash" from 1st/4th and 2nd/3rd
221 $modulaHashes = array(
222         // "Block" 0
223         modulaHash($hashes[0], $hashes[3]),
224         modulaHash($hashes[1], $hashes[2]),
225
226         // "Block" 1
227         modulaHash($hashes[4], $hashes[7]),
228         modulaHash($hashes[5], $hashes[6]),
229 );
230
231 // Calculate "sqrt hash"
232 $sqrtHashes = array(
233         sqrtHash($modulaHashes[0], $modulaHashes[1]),
234         sqrtHash($modulaHashes[2], $modulaHashes[3])
235 );
236
237 // Calulcate modula hash
238 $modulaHash = doubleHashString(modulaHash($sqrtHashes[0], $sqrtHashes[1]));
239
240 // Output results
241 print ('hashes=' . print_r($hashes, TRUE));
242 print ('modulaHashes=' . print_r($modulaHashes, TRUE));
243 print ('sqrtHashes=' . print_r($sqrtHashes, TRUE));
244 print ('modulaHash=' . $modulaHash . PHP_EOL);
245
246 // Total reward + hashes
247 $totalReward = 0;
248 $totalHashes = 0;
249 $totalBlocks = 0;
250
251 // Create nonce (small)
252 $nonce = 1 / mt_rand();
253
254 while (TRUE) {
255         // Init hash-per-block counter and hashrate
256         $hashesPerBlock = 0;
257         $hashrate = 0;
258
259         // Wait for BLOCK_SIZE iterations (= found hashes). This is one block
260         $timeBlock = microtime(TRUE);
261         $timeDisplay = $timeBlock;
262
263         // Time waited for a good block again (no iteration)
264         $timeBadHashes = 0;
265
266         while ($hashesPerBlock <= getBlockSize()) {
267                 // Init counter
268                 $iter = 0;
269
270                 // Create hash from modulaHash ("genesis hash") and nonce
271                 $nonceHash = doubleHashString($modulaHash . $nonce);
272
273                 // Calculate sums
274                 $sumNonce  = calculateSumFromHash($nonceHash);
275                 $sumModula = calculateSumFromHash($modulaHash);
276
277                 // Now start the "mining" ...
278                 $timeHash = microtime(TRUE);
279                 while ($sumNonce <= $sumModula) {
280                         // Calculate sums
281                         $sumNonce  = calculateSumFromHash($nonceHash);
282                         $sumModula = calculateSumFromHash($modulaHash);
283
284                         // Calculate new nonce
285                         $nonce = calculateNonce($nonce);
286
287                         // And hash again
288                         $nonceHash = doubleHashString($modulaHash . $nonce);
289
290                         // Next round
291                         $iter++;
292                         //print ('nonce=' . $nonce . ',iter=' . $iter . PHP_EOL);
293                         //print ('nonceHash=' . $nonceHash . PHP_EOL);
294                         //print ('sumNonce=' . $sumNonce . PHP_EOL);
295                         //print ('sumModula=' . $sumModula . PHP_EOL);
296                 } // END - while
297
298                 // If the iteration is zero, then no hash is found
299                 if ($iter == 0) {
300                         // Bad block found
301                         $timeBadHashes += abs(microtime(TRUE) - $timeHash);
302
303                         // Nothing found, so calculate new nonce
304                         $nonce = calculateNonce($nonce);
305
306                         // And next round
307                         continue;
308                 } // END - if
309
310                 // Add amount of hashes to block (double-hash)
311                 $hashesPerBlock += $iter * 2 + 2;
312
313                 // Time spend in loop
314                 $testTime = abs(microtime(TRUE) - $timeDisplay);
315
316                 // Only every second
317                 if ($testTime >= 1) {
318                         // Calculate hashrate/sec
319                         $timeHash = abs(microtime(TRUE) - $timeHash);
320                         $hashrate = 1 / $timeHash * $iter * 2 + 2;
321                         print ('hashesPerBlock=' . $hashesPerBlock . ',hashrate=' . $hashrate . ' hashes/sec.' . PHP_EOL);
322
323                         // Reset timer
324                         $timeDisplay = microtime(TRUE); 
325                 } // END - if
326
327                 // Found hash:
328                 //print ('nonceHash=' . $nonceHash .',iter=' . $iter . PHP_EOL);
329         } // END - while
330
331         // Time taken for one block
332         $timeBlock = abs(microtime(TRUE) - $timeBlock);
333         //print ('calculateSumFromHash(modulaHash)=' . calculateSumFromHash($modulaHash) . PHP_EOL);
334         //print ('calculateSumFromHash(nonceHash)=' . calculateSumFromHash($nonceHash) . PHP_EOL);
335
336         // Is the hash rate unset?
337         if ($hashrate == 0) {
338                 // Then calculate it again
339                 $hashrate = 1 / $timeBlock * $iter * 2 + 2;
340         } // END - if
341
342         // Calculate reward
343         $reward = abs($timeBlock - $timeBadHashes) / $hashrate * $hashesPerBlock / getBlockSize() * 1000;
344         print ('timeBlock=' . $timeBlock . ',timeBadHashes=' . $timeBadHashes . ',hashesPerBlock=' . $hashesPerBlock .',reward=' . $reward . PHP_EOL);
345
346         // Block completed
347         $totalHashes += $hashesPerBlock;
348         $totalBlocks++;
349         $hashesPerBlock = 0;
350
351         // Use nonceHash as next modula hash
352         $modulaHash = $nonceHash;
353
354         // Add reward to total
355         $totalReward += $reward;
356
357         // Calculate average block value
358         $blockValue = $totalReward / $totalBlocks * $totalHashes / (getBlockSize() * $totalBlocks);
359
360         // Calculate reward per hour (= 3600 seconds)
361         $rewardPerHour = $totalReward / abs(microtime(TRUE) - START_TIME) * 3600;
362
363         print ('totalReward=' . $totalReward . ',blockValue=' . $blockValue . ',rewardPerHour=' . $rewardPerHour . PHP_EOL);
364 } // END - while
365
366 // [EOF]
367 ?>