PageRenderTime 59ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/v1.9/treasurer.php

https://bitbucket.org/rev22/timekoin
PHP | 557 lines | 387 code | 81 blank | 89 comment | 99 complexity | 33fe588854b47f0e16920174db36d6a0 MD5 | raw file
  1. <?PHP
  2. include 'configuration.php';
  3. include 'function.php';
  4. set_time_limit(60);
  5. //***********************************************************************************
  6. //***********************************************************************************
  7. if(TREASURER_DISABLED == TRUE || TIMEKOIN_DISABLED == TRUE)
  8. {
  9. // This has been disabled
  10. exit;
  11. }
  12. //***********************************************************************************
  13. //***********************************************************************************
  14. mysql_connect(MYSQL_IP,MYSQL_USERNAME,MYSQL_PASSWORD);
  15. mysql_select_db(MYSQL_DATABASE);
  16. $loop_active = mysql_result(mysql_query("SELECT * FROM `main_loop_status` WHERE `field_name` = 'treasurer_heartbeat_active' LIMIT 1"),0,"field_data");
  17. // Check if loop is already running
  18. if($loop_active == 0)
  19. {
  20. // Set the working status of 1
  21. mysql_query("UPDATE `main_loop_status` SET `field_data` = '1' WHERE `main_loop_status`.`field_name` = 'treasurer_heartbeat_active' LIMIT 1");
  22. }
  23. else
  24. {
  25. // Loop called while still working
  26. exit;
  27. }
  28. //***********************************************************************************
  29. //***********************************************************************************
  30. $previous_generation_cycle = transaction_cycle(-1);
  31. $current_generation_cycle = transaction_cycle(0);
  32. $next_generation_cycle = transaction_cycle(1);
  33. $current_generation_block = transaction_cycle(0, TRUE);
  34. //*****************************************************************************************************
  35. //*****************************************************************************************************
  36. // Check my transaction queue and copy pending transaction to the main transaction queue
  37. $sql = "SELECT * FROM `my_transaction_queue` LIMIT 100";
  38. $sql_result = mysql_query($sql);
  39. $sql_num_results = mysql_num_rows($sql_result);
  40. if($sql_num_results > 0)
  41. {
  42. // Can we copy my transaction queue to the main queue in the allowed time?
  43. // Not allowed 120 seconds before and 30 seconds after generation cycle.
  44. if(($next_generation_cycle - time()) > 120 && (time() - $current_generation_cycle) > 30)
  45. {
  46. $firewall_blocked = mysql_result(mysql_query("SELECT * FROM `options` WHERE `field_name` = 'firewall_blocked_peer' LIMIT 1"),0,"field_data");
  47. for ($i = 0; $i < $sql_num_results; $i++)
  48. {
  49. $sql_row = mysql_fetch_array($sql_result);
  50. $time_created = $sql_row["timestamp"];
  51. $public_key = $sql_row["public_key"];
  52. $crypt1 = $sql_row["crypt_data1"];
  53. $crypt2 = $sql_row["crypt_data2"];
  54. $crypt3 = $sql_row["crypt_data3"];
  55. $hash_check = $sql_row["hash"];
  56. $attribute = $sql_row["attribute"];
  57. // Check to see if transaction is saved in the transaction history
  58. openssl_public_decrypt(base64_decode($crypt1), $public_key_to_1, $public_key);
  59. openssl_public_decrypt(base64_decode($crypt2), $public_key_to_2, $public_key);
  60. $public_key_to = $public_key_to_1 . $public_key_to_2;
  61. $found_transaction_history = mysql_result(mysql_query("SELECT * FROM `transaction_history` WHERE `public_key_from` = '$public_key'
  62. AND `public_key_to` = '$public_key_to' AND `hash` = '$hash_check' LIMIT 1"),0,"timestamp");
  63. if(empty($found_transaction_history) == FALSE)
  64. {
  65. // This transaction is in the history now, let's wait about 2 hours before clearing
  66. // this from the transaction queue in case of network congestion or other factors
  67. // that somehow prevented the transaction from making into the network peer swarm
  68. if(time() - $found_transaction_history > 7200) // Recycle the variable ;)
  69. {
  70. $sql = "DELETE FROM `my_transaction_queue` WHERE `my_transaction_queue`.`public_key` = '$public_key' AND `my_transaction_queue`.`hash` = '$hash_check' LIMIT 1";
  71. if(mysql_query($sql) == FALSE)
  72. {
  73. //Something didn't work
  74. write_log("Could NOT Delete A Transaction Copy from MyQueue", "TR");
  75. }
  76. }
  77. }
  78. else
  79. {
  80. // Check to make sure there is not a duplicate transaction already
  81. $found_public_key_queue = mysql_result(mysql_query("SELECT * FROM `transaction_queue` WHERE `public_key` = '$public_key' AND `hash` = '$hash_check' LIMIT 1"),0,"timestamp");
  82. $timestamp = $current_generation_cycle + 9;
  83. if(empty($found_public_key_queue) == TRUE)
  84. {
  85. if($firewall_blocked == "1" || rand(1,3) != 2)// Mix outbound transaction broadcasting and regular polling
  86. {
  87. // We are stuck behind a firewall with no inbound connections.
  88. // The best we can do is try to submit our transaction out to peer
  89. // that is accepting inbound connections and hopefully that will replicate
  90. // out to the peer network.
  91. ini_set('user_agent', 'Timekoin Server (Treasurer) v' . TIMEKOIN_VERSION);
  92. ini_set('default_socket_timeout', 3); // Timeout for request in seconds
  93. $sql = "SELECT * FROM `active_peer_list` ORDER BY RAND() LIMIT 1";
  94. $sql_row = mysql_fetch_array(mysql_query($sql));
  95. $ip_address = $sql_row["IP_Address"];
  96. $domain = $sql_row["domain"];
  97. $subfolder = $sql_row["subfolder"];
  98. $port_number = $sql_row["port_number"];
  99. if(empty($domain) == TRUE)
  100. {
  101. $site_address = $ip_address;
  102. }
  103. else
  104. {
  105. $site_address = $domain;
  106. }
  107. $qhash = $timestamp . base64_encode($public_key) . $crypt1 . $crypt2 . $crypt3 . $hash_check . $attribute;
  108. $qhash = hash('md5', $qhash);
  109. // Create map with request parameters
  110. $params = array ('timestamp' => $timestamp,
  111. 'public_key' => base64_encode($public_key),
  112. 'crypt_data1' => $crypt1,
  113. 'crypt_data2' => $crypt2,
  114. 'crypt_data3' => $crypt3,
  115. 'hash' => $hash_check,
  116. 'attribute' => $attribute,
  117. 'qhash' => $qhash);
  118. // Build Http query using params
  119. $query = http_build_query($params);
  120. // Create Http context details
  121. $contextData = array (
  122. 'method' => 'POST',
  123. 'header' => "Connection: close\r\n".
  124. "Content-Length: ".strlen($query)."\r\n",
  125. 'content'=> $query );
  126. // Create context resource for our request
  127. $context = stream_context_create (array ( 'http' => $contextData ));
  128. if($port_number == 443)
  129. {
  130. $ssl = "s";
  131. }
  132. else
  133. {
  134. $ssl = NULL;
  135. }
  136. // Read page rendered as result of the POST request
  137. $poll_peer = filter_sql(file_get_contents("http$ssl://$site_address:$port_number/$subfolder/queueclerk.php?action=input_transaction", FALSE, $context));
  138. if($poll_peer == "OK")
  139. {
  140. // Insert to the peer remotely was accepted
  141. switch($attribute)
  142. {
  143. case "R":
  144. write_log("Join Generation Peer Request Insert accepted by remote Peer $site_address:$port_number/$subfolder", "R");
  145. break;
  146. case "G":
  147. write_log("Timekoin Generation Insert accepted by remote Peer $site_address:$port_number/$subfolder", "G");
  148. break;
  149. case "T":
  150. write_log("Standard Transaction Insert accepted by remote Peer $site_address:$port_number/$subfolder", "T");
  151. break;
  152. }
  153. }
  154. else
  155. {
  156. // Failed, probably due to no inbound connection allowed at the other peer
  157. switch($attribute)
  158. {
  159. case "R":
  160. write_log("Join Generation Peer Request Insert FAILED for remote Peer $site_address:$port_number/$subfolder", "R");
  161. break;
  162. case "G":
  163. write_log("Timekoin Generation Insert FAILED for remote Peer $site_address:$port_number/$subfolder", "G");
  164. break;
  165. case "T":
  166. write_log("Standard Transaction Insert FAILED for remote Peer $site_address:$port_number/$subfolder", "T");
  167. break;
  168. }
  169. }
  170. } // Firewall Mode Check
  171. else
  172. {
  173. // Full Internet exposure
  174. $sql = "INSERT INTO `transaction_queue` (`timestamp`,`public_key`,`crypt_data1`,`crypt_data2`,`crypt_data3`, `hash`, `attribute`)
  175. VALUES ('" . $timestamp . "', '$public_key', '$crypt1', '$crypt2' , '$crypt3', '$hash_check' , '$attribute');";
  176. if(mysql_query($sql) == TRUE)
  177. {
  178. switch($attribute)
  179. {
  180. case "R":
  181. write_log("Join Generation Peer Request Insert from MyQueue Complete", "R");
  182. break;
  183. case "G":
  184. write_log("Timekoin Generation Insert from MyQueue Complete", "G");
  185. break;
  186. case "T":
  187. write_log("Standard Transaction Insert from MyQueue Complete", "T");
  188. break;
  189. }
  190. }
  191. }
  192. }
  193. } // End Duplicate in Transaction History Check
  194. } // End for loop
  195. } // End timing allowed check
  196. }
  197. //*****************************************************************************************************
  198. //*****************************************************************************************************
  199. // Find all transactions between the Previous Transaction Cycle and the Current
  200. $sql = "SELECT * FROM `transaction_queue` WHERE `timestamp` >= $previous_generation_cycle AND `timestamp` < $current_generation_cycle ORDER BY `attribute`";
  201. $sql_result = mysql_query($sql);
  202. $sql_num_results = mysql_num_rows($sql_result);
  203. if($sql_num_results > 0)
  204. {
  205. for ($i = 0; $i < $sql_num_results; $i++)
  206. {
  207. $sql_row = mysql_fetch_array($sql_result);
  208. $safe_delete_transaction = FALSE;
  209. $public_key = $sql_row["public_key"];
  210. $hash_check = $sql_row["hash"];
  211. //Copy transaction to final transaction history
  212. if($sql_row["attribute"] == "G") // Currency Generation Transaction
  213. {
  214. $str = strval($current_generation_cycle);
  215. $last3_gen = $str[strlen($str)-3];
  216. TKRandom::seed($current_generation_block);
  217. $tk_random_number = TKRandom::num(0, 9);
  218. // Random generation time that can be duplicated across all servers
  219. if($last3_gen + $tk_random_number < 10)
  220. {
  221. // Is this public key allowed to generate currency?
  222. $generation_public_key = mysql_result(mysql_query("SELECT * FROM `generating_peer_list` WHERE `public_key` = '$public_key' LIMIT 1"),0,"public_key");
  223. if(empty($generation_public_key) == TRUE)
  224. {
  225. //Not allowed to generate currency
  226. write_log("Key Not in Generation Peer List: " . base64_encode($public_key), "G");
  227. }
  228. else
  229. {
  230. // Check to make sure there is not a duplicate generation transaction already
  231. $found_public_key_queue = mysql_result(mysql_query("SELECT * FROM `transaction_history` WHERE `public_key_from` = '$public_key' AND `attribute` = 'G' AND `timestamp` >= $previous_generation_cycle AND `timestamp` < $current_generation_cycle LIMIT 1"),0,"timestamp");
  232. if(empty($found_public_key_queue) == TRUE)
  233. {
  234. // Check to make sure enough time has passed since this public key joined the network to allow currency generation
  235. // Default is 1 Hour or 3600 seconds
  236. $join_peer_list = mysql_result(mysql_query("SELECT * FROM `generating_peer_list` WHERE `public_key` = '$public_key' LIMIT 1"),0,"join_peer_list");
  237. if((time() - $join_peer_list) >= 3600) // It's been more than 3600 seconds since this public key joined the generating peer list
  238. {
  239. $time_created = $sql_row["timestamp"];
  240. $crypt1 = $sql_row["crypt_data1"];
  241. $crypt2 = $sql_row["crypt_data2"];
  242. $crypt3 = $sql_row["crypt_data3"];
  243. $hash_check = $sql_row["hash"];
  244. // Check generation amount to make sure it has not been tampered with
  245. openssl_public_decrypt(base64_decode($crypt3), $transaction_info, $public_key);
  246. $transaction_amount_sent = find_string("AMOUNT=", "---TIME", $transaction_info);
  247. $transaction_amount_sent_test = intval($transaction_amount_sent);
  248. if($transaction_amount_sent_test == $transaction_amount_sent && $transaction_amount_sent > 0)
  249. {
  250. // Is a valid integer
  251. $amount_valid = TRUE;
  252. }
  253. else
  254. {
  255. // Is NOT a valid integer
  256. $amount_valid = FALSE;
  257. }
  258. if($transaction_amount_sent <= peer_gen_amount($public_key) && $amount_valid == TRUE)
  259. {
  260. // Everything checks out for valid integer and generation amount
  261. }
  262. else
  263. {
  264. // Either the amount to generate was wrong or the amount itself is not an integer
  265. $amount_valid = FALSE;
  266. }
  267. if(hash('sha256', $crypt1 . $crypt2 . $crypt3) == $hash_check && $amount_valid == TRUE) // Hash check for tampering
  268. {
  269. // Public key not found, insert into final transaction history
  270. $sql = "INSERT INTO `transaction_history` (`timestamp` ,`public_key_from`, `public_key_to` ,`crypt_data1` ,`crypt_data2` ,`crypt_data3` ,`hash` ,`attribute`)
  271. VALUES ($time_created, '$public_key', '$public_key', '$crypt1', '$crypt2', '$crypt3', '$hash_check', 'G');";
  272. if(mysql_query($sql) == FALSE)
  273. {
  274. //Something didn't work
  275. write_log("Generation Insert Failed for this Key:" . base64_encode($public_key), "G");
  276. }
  277. // Update the last generation timestamp
  278. $sql = "UPDATE `generating_peer_list` SET `last_generation` = '$current_generation_cycle' WHERE `generating_peer_list`.`public_key` = '$public_key' LIMIT 1";
  279. if(mysql_query($sql) == FALSE)
  280. {
  281. //Something didn't work
  282. write_log("Generation Timestamp Update Failed for this Key:" . base64_encode($public_key), "G");
  283. }
  284. }
  285. else
  286. {
  287. // Failed Hash check or Valid Amount check
  288. write_log("Generation Hash or Amount Check Failed for this Key:" . base64_encode($public_key), "G");
  289. }
  290. }
  291. else
  292. {
  293. // Not enough time has passed
  294. write_log("Generation Too Early for this Key:" . base64_encode($public_key), "G");
  295. }
  296. }
  297. else
  298. {
  299. // Duplicate generation transaction already exist
  300. write_log("Generation Duplicate Discarded for this Key:" . base64_encode($public_key), "G");
  301. }
  302. }// End generation allowed check
  303. } // End random time check
  304. } // End Transaction type G check
  305. if($sql_row["attribute"] == "T") // Regular Transaction
  306. {
  307. // Check to make sure there is not a duplicate transaction already
  308. $found_public_key_queue = mysql_result(mysql_query("SELECT * FROM `transaction_history` WHERE `public_key_from` = '$public_key' AND `hash` = '$hash_check' LIMIT 1"),0,"timestamp");
  309. if(empty($found_public_key_queue) == TRUE)
  310. {
  311. // Transaction isn't a duplicate, continue processing...
  312. $time_created = $sql_row["timestamp"];
  313. $crypt1 = $sql_row["crypt_data1"];
  314. $crypt2 = $sql_row["crypt_data2"];
  315. $crypt3 = $sql_row["crypt_data3"];
  316. // How much is this public key trying to send to another public key?
  317. openssl_public_decrypt(base64_decode($crypt3), $transaction_info, $public_key);
  318. $transaction_amount_sent = find_string("AMOUNT=", "---TIME", $transaction_info);
  319. $transaction_amount_sent_test = intval($transaction_amount_sent);
  320. if($transaction_amount_sent_test == $transaction_amount_sent)
  321. {
  322. // Is a valid integer
  323. $amount_valid = TRUE;
  324. }
  325. else
  326. {
  327. // Is NOT a valid integer
  328. $amount_valid = FALSE;
  329. }
  330. // Validate transaction against known public key balance
  331. if(check_crypt_balance($public_key) >= $transaction_amount_sent && $transaction_amount_sent > 0 && $amount_valid == TRUE)
  332. {
  333. // Balance checks out
  334. // Check hash value for tampering of crypt1, crypt2, or crypt3 fields
  335. if(hash('sha256', $crypt1 . $crypt2 . $crypt3) == $hash_check)
  336. {
  337. // Find destination public key
  338. openssl_public_decrypt(base64_decode($crypt1), $public_key_to_1, $public_key);
  339. openssl_public_decrypt(base64_decode($crypt2), $public_key_to_2, $public_key);
  340. $public_key_to = $public_key_to_1 . $public_key_to_2;
  341. if(strlen($public_key) > 256 && strlen($public_key_to) > 256 && $public_key !== $public_key_to) // Filter to/from self public keys
  342. {
  343. // Public key not found, insert into final transaction history
  344. $sql = "INSERT INTO `transaction_history` (`timestamp` ,`public_key_from` , `public_key_to` , `crypt_data1` ,`crypt_data2` ,`crypt_data3` ,`hash` ,`attribute`)
  345. VALUES ($time_created, '$public_key', '$public_key_to' , '$crypt1', '$crypt2', '$crypt3', '$hash_check', 'T');";
  346. if(mysql_query($sql) == FALSE)
  347. {
  348. //Something didn't work
  349. write_log("Transaction Database Insert Failed for this Key:" . base64_encode($public_key), "T");
  350. }
  351. }
  352. else
  353. {
  354. // Invalid or blank Public Key(s)
  355. write_log("Transaction Public Key Error for this Key:" . base64_encode($public_key), "T");
  356. $safe_delete_transaction = TRUE;
  357. }
  358. }
  359. else
  360. {
  361. // Hash check failed
  362. write_log("Transaction Hash Check Failed for this Key:" . base64_encode($public_key), "T");
  363. $safe_delete_transaction = TRUE;
  364. }
  365. }
  366. else
  367. {
  368. // Balance is incorrect, transaction invalid
  369. write_log("Transaction Balance Check Failed for this Key:" . base64_encode($public_key), "T");
  370. $safe_delete_transaction = TRUE;
  371. }
  372. }
  373. else
  374. {
  375. // Duplicate Transaction
  376. write_log("Duplicate Transaction Failed for this Key:" . base64_encode($public_key), "T");
  377. $safe_delete_transaction = TRUE;
  378. }
  379. } // Regular Transaction Check
  380. // Safe to delete transaction from my transaction queue?
  381. if($safe_delete_transaction == TRUE)
  382. {
  383. // Delete any copies from my transaction queue
  384. $sql = "DELETE FROM `my_transaction_queue` WHERE `my_transaction_queue`.`public_key` = '$public_key' AND `my_transaction_queue`.`hash` = '$hash_check' LIMIT 1";
  385. if(mysql_query($sql) == FALSE)
  386. {
  387. //Something didn't work
  388. write_log("Could NOT Delete A Transaction Copy from MyQueue", "TR");
  389. }
  390. }
  391. } // End for Loop
  392. // Wipe transaction queue of all old transaction from current to previous cycle
  393. $sql = "DELETE FROM `transaction_queue` WHERE `transaction_queue`.`timestamp` < $current_generation_cycle";
  394. if(mysql_query($sql) == FALSE)
  395. {
  396. //Something didn't work
  397. write_log("Could NOT Delete Old Transactions from the Transaction Queue", "TR");
  398. }
  399. $sql = "DELETE FROM `my_transaction_queue` WHERE `my_transaction_queue`.`attribute` = 'R' OR `my_transaction_queue`.`attribute` = 'G'";
  400. if(mysql_query($sql) == FALSE)
  401. {
  402. //Something didn't work
  403. write_log("Could NOT Delete Old Generation Join Request or Currency Generation from the MyQueue", "TR");
  404. }
  405. }
  406. else
  407. {
  408. // Wipe transaction that are too old to be used in the next transaction cycle
  409. $sql = "DELETE FROM `transaction_queue` WHERE `transaction_queue`.`timestamp` < $previous_generation_cycle";
  410. mysql_query($sql);
  411. }
  412. //***********************************************************************************
  413. // Check to see if it is time to write a hash of the last cycle transactions
  414. $generation_arbitrary = ARBITRARY_KEY;
  415. $current_hash = mysql_result(mysql_query("SELECT * FROM `transaction_history` WHERE `timestamp` >= $current_generation_cycle AND `timestamp` < $next_generation_cycle AND `attribute` = 'H' LIMIT 1"),0,"hash");
  416. $past_hash = mysql_result(mysql_query("SELECT * FROM `transaction_history` WHERE `timestamp` >= $previous_generation_cycle AND `timestamp` < $current_generation_cycle AND `attribute` = 'H' LIMIT 1"),0,"hash");
  417. if(empty($current_hash) == TRUE)
  418. {
  419. if(empty($past_hash) == TRUE)
  420. {
  421. //The past cycle hash is missing, can't move forward without it
  422. }
  423. else
  424. {
  425. //A hash from the previous generation cycle does not exist yet, so create it
  426. $sql = "SELECT * FROM `transaction_history` WHERE `timestamp` >= $previous_generation_cycle AND `timestamp` < $current_generation_cycle ORDER BY `timestamp`, `hash`";
  427. $sql_result = mysql_query($sql);
  428. $sql_num_results = mysql_num_rows($sql_result);
  429. $hash = 0;
  430. if($sql_num_results == 0)
  431. {
  432. // Transaction history is incomplete
  433. }
  434. else
  435. {
  436. for ($i = 0; $i < $sql_num_results; $i++)
  437. {
  438. $sql_row = mysql_fetch_array($sql_result);
  439. $hash .= $sql_row["hash"];
  440. }
  441. // Transaction hash
  442. $hash = hash('sha256', $hash);
  443. $sql = "INSERT INTO `transaction_history` (`timestamp` ,`public_key_from` ,`public_key_to` ,`crypt_data1` ,`crypt_data2` ,`crypt_data3` ,`hash` ,`attribute`)
  444. VALUES ('$current_generation_cycle', '$generation_arbitrary', '$generation_arbitrary', '$generation_arbitrary', '$generation_arbitrary', '$generation_arbitrary', '$hash', 'H')";
  445. mysql_query($sql);
  446. // Update Transaction History Hash
  447. $total = mysql_query("SELECT COUNT(*) FROM `transaction_history`");
  448. $total = mysql_fetch_array($total);
  449. $hash = $total[0];
  450. $previous_foundation_block = foundation_cycle(-1, TRUE);
  451. $current_foundation_cycle = foundation_cycle(0);
  452. $next_foundation_cycle = foundation_cycle(1);
  453. $current_history_foundation = mysql_result(mysql_query("SELECT * FROM `transaction_foundation` WHERE `block` = $previous_foundation_block LIMIT 1"),0,"hash");
  454. $hash = $hash . $current_history_foundation;
  455. $sql = "SELECT * FROM `transaction_history` WHERE `timestamp` >= $current_foundation_cycle AND `timestamp` < $next_foundation_cycle AND `attribute` = 'H' ORDER BY `timestamp`";
  456. $sql_result = mysql_query($sql);
  457. $sql_num_results = mysql_num_rows($sql_result);
  458. for ($i = 0; $i < $sql_num_results; $i++)
  459. {
  460. $sql_row = mysql_fetch_array($sql_result);
  461. $hash .= $sql_row["hash"];
  462. }
  463. $history_hash = hash('md5', $hash);
  464. // Update database with new hash
  465. $sql = "UPDATE `options` SET `field_data` = '$history_hash' WHERE `field_name` = 'transaction_history_hash' LIMIT 1";
  466. mysql_query($sql);
  467. } // End Previous Hash Missing Check
  468. } // Pass hash check for existance
  469. } // End Empty Hash Check
  470. //***********************************************************************************
  471. //***********************************************************************************
  472. // Script finished, set status to 0
  473. mysql_query("UPDATE `main_loop_status` SET `field_data` = 0 WHERE `main_loop_status`.`field_name` = 'treasurer_heartbeat_active' LIMIT 1");
  474. // Record when this script finished
  475. mysql_query("UPDATE `main_loop_status` SET `field_data` = " . time() . " WHERE `main_loop_status`.`field_name` = 'treasurer_last_heartbeat' LIMIT 1");
  476. ?>