PageRenderTime 26ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/v1.9/foundation.php

https://bitbucket.org/rev22/timekoin
PHP | 384 lines | 249 code | 62 blank | 73 comment | 71 complexity | a6fac757653d5d29892fc72fac2479fc MD5 | raw file
  1. <?PHP
  2. include 'configuration.php';
  3. include 'function.php';
  4. set_time_limit(100);
  5. //***********************************************************************************
  6. //***********************************************************************************
  7. if(FOUNDATION_DISABLED == TRUE || TIMEKOIN_DISABLED == TRUE)
  8. {
  9. // This has been disabled
  10. exit;
  11. }
  12. //***********************************************************************************
  13. //***********************************************************************************
  14. // Open persistent connection to database
  15. mysql_connect(MYSQL_IP,MYSQL_USERNAME,MYSQL_PASSWORD);
  16. mysql_select_db(MYSQL_DATABASE);
  17. // Check for banned IP address
  18. $ip = mysql_result(mysql_query("SELECT * FROM `ip_banlist` WHERE `ip` = '" . $_SERVER['REMOTE_ADDR'] . "' LIMIT 1"),0,0);
  19. if(empty($ip) == FALSE)
  20. {
  21. // Sorry, your IP address has been banned :(
  22. exit;
  23. }
  24. //***********************************************************************************
  25. //***********************************************************************************
  26. // Answer block hash poll
  27. if($_GET["action"] == "block_hash" && $_GET["block_number"] >= 0)
  28. {
  29. $block_number = intval($_GET["block_number"]);
  30. $hash = mysql_result(mysql_query("SELECT * FROM `transaction_foundation` WHERE `block` = $block_number LIMIT 1"),0,"hash");
  31. echo $hash;
  32. // Log inbound IP activity
  33. mysql_query("INSERT INTO `ip_activity` (`timestamp` ,`ip`, `attribute`)VALUES ('" . time() . "', '" . $_SERVER['REMOTE_ADDR'] . "', 'FO')");
  34. exit;
  35. }
  36. //***********************************************************************************
  37. //***********************************************************************************
  38. $loop_active = mysql_result(mysql_query("SELECT * FROM `main_loop_status` WHERE `field_name` = 'foundation_heartbeat_active' LIMIT 1"),0,"field_data");
  39. // Check if loop is already running
  40. if($loop_active == 0)
  41. {
  42. // Set the working status of 1
  43. mysql_query("UPDATE `main_loop_status` SET `field_data` = '1' WHERE `main_loop_status`.`field_name` = 'foundation_heartbeat_active' LIMIT 1");
  44. }
  45. else
  46. {
  47. // Loop called while still working
  48. exit;
  49. }
  50. //***********************************************************************************
  51. //***********************************************************************************
  52. $previous_foundation_block = foundation_cycle(-1, TRUE);
  53. $current_foundation_block = foundation_cycle(0, TRUE);
  54. $current_generation_cycle = transaction_cycle(0);
  55. $current_generation_block = transaction_cycle(0, TRUE);
  56. $next_generation_cycle = transaction_cycle(1);
  57. $total = mysql_query("SELECT COUNT(*) FROM `transaction_history`");
  58. $total = mysql_fetch_array($total);
  59. $record_count = $total[0];
  60. if($record_count < $current_generation_block)
  61. {
  62. // Not enough records to warrant even doing foundation building or checking
  63. $foundation_task = 1;
  64. }
  65. else
  66. {
  67. $foundation_task = mysql_result(mysql_query("SELECT * FROM `options` WHERE `field_name` = 'foundation_block_check' LIMIT 1"),0,"field_data");
  68. }
  69. // Can we work on the transactions in the database?
  70. // Not allowed 60 seconds before and 60 seconds after generation cycle.
  71. // Don't build anything if a foundation check is already going on.
  72. if(($next_generation_cycle - time()) > 60 && (time() - $current_generation_cycle) > 60 && $foundation_task == 0)
  73. {
  74. //***********************************************************************************
  75. // Does my current history hash match all my peers?
  76. // Ask all of my active peers
  77. ini_set('default_socket_timeout', 3); // Timeout for request in seconds
  78. ini_set('user_agent', 'Timekoin Server (Foundation) v' . TIMEKOIN_VERSION);
  79. $context = stream_context_create(array('http' => array('header'=>'Connection: close'))); // Force close socket after complete
  80. $sql = "SELECT * FROM `active_peer_list`";
  81. $sql_result = mysql_query($sql);
  82. $sql_num_results = mysql_num_rows($sql_result);
  83. $foundation_hash_match = 0;
  84. $foundation_hash_different = 0;
  85. $site_address;
  86. if($sql_num_results > 0)
  87. {
  88. // Choose random transaction foundation
  89. if(rand(1,3) == 3)
  90. {
  91. // Check the most recent foundations more frequently than older foundations
  92. $rand_block = rand($previous_foundation_block - 4,$previous_foundation_block);
  93. }
  94. else
  95. {
  96. $rand_block = rand(0,$previous_foundation_block);
  97. }
  98. $current_foundation_hash = mysql_result(mysql_query("SELECT * FROM `transaction_foundation` WHERE `block` = $rand_block LIMIT 1"),0,"hash");
  99. // Make sure we even have a hash to compare against
  100. if(empty($current_foundation_hash) == FALSE)
  101. {
  102. for ($i = 0; $i < $sql_num_results; $i++)
  103. {
  104. $sql_row = mysql_fetch_array($sql_result);
  105. $ip_address = $sql_row["IP_Address"];
  106. $domain = $sql_row["domain"];
  107. $subfolder = $sql_row["subfolder"];
  108. $port_number = $sql_row["port_number"];
  109. if(empty($domain) == TRUE)
  110. {
  111. $site_address = $ip_address;
  112. }
  113. else
  114. {
  115. $site_address = $domain;
  116. }
  117. if($port_number == 443)
  118. {
  119. $ssl = "s";
  120. }
  121. else
  122. {
  123. $ssl = NULL;
  124. }
  125. $poll_peer = filter_sql(file_get_contents("http$ssl://$site_address:$port_number/$subfolder/foundation.php?action=block_hash&block_number=$rand_block", FALSE, $context, NULL, 65));
  126. if($current_foundation_hash === $poll_peer)
  127. {
  128. $foundation_hash_match++;
  129. }
  130. else
  131. {
  132. if(empty($poll_peer) == FALSE && strlen($poll_peer) > 50)
  133. {
  134. $foundation_hash_different++;
  135. }
  136. }
  137. } // End for Loop
  138. // Compare tallies
  139. if($rand_block == $previous_foundation_block)
  140. {
  141. // 2/3 of the peers must disagree to schedule a block wipe/repair
  142. // this recent.
  143. if($foundation_hash_different == 0)
  144. {
  145. // No peers disagrees, all is well
  146. $repair_block = FALSE;
  147. }
  148. else
  149. {
  150. if($foundation_hash_different / $sql_num_results >= 2 / 3)
  151. {
  152. $repair_block = TRUE;
  153. }
  154. }
  155. }
  156. else
  157. {
  158. // Anything deeper than +1 block back requires 100% of the peers
  159. // to disagree before a block wipe/repair is scheduled.
  160. if($foundation_hash_match == 0)
  161. {
  162. $repair_block = TRUE;
  163. }
  164. }
  165. if($repair_block == TRUE)
  166. {
  167. write_log("Invalid Foundation Block Found, Starting Repair for #$rand_block", "FO");
  168. // Start by removing the transaction foundation block hash
  169. $sql = "DELETE FROM `transaction_foundation` WHERE `transaction_foundation`.`block` = $rand_block LIMIT 1";
  170. if(mysql_query($sql) == TRUE)
  171. {
  172. // Now wipe the range of transactions for this block
  173. $foundation_time_start = $rand_block * 500;
  174. $foundation_time_end = ($rand_block * 500) + 500;
  175. if($rand_block > 0)
  176. {
  177. $time1 = transaction_cycle(0 - $current_generation_block + $foundation_time_start);
  178. }
  179. else
  180. {
  181. // Prevent the Beginning Block from being wiped when repairing #0
  182. $time1 = 1338576600;
  183. }
  184. $time2 = transaction_cycle(0 - $current_generation_block + $foundation_time_end);
  185. $sql = "DELETE FROM `transaction_history` WHERE `timestamp` >= $time1 AND `timestamp` <= $time2";
  186. if(mysql_query($sql) == TRUE)
  187. {
  188. // Schedule a block check starting at the first block the problem occurs
  189. $sql = "UPDATE `options` SET `field_data` = '1' WHERE `field_name` = 'foundation_block_check' LIMIT 1";
  190. if(mysql_query($sql) == TRUE)
  191. {
  192. mysql_query("UPDATE `options` SET `field_data` = '0' WHERE `options`.`field_name` = 'block_check_start' LIMIT 1");
  193. mysql_query("UPDATE `options` SET `field_data` = '1' WHERE `options`.`field_name` = 'block_check_back' LIMIT 1");
  194. mysql_query("UPDATE `options` SET `field_data` = '$foundation_time_start' WHERE `options`.`field_name` = 'foundation_block_check_start' LIMIT 1");
  195. mysql_query("UPDATE `options` SET `field_data` = '$foundation_time_end' WHERE `options`.`field_name` = 'foundation_block_check_end' LIMIT 1");
  196. }
  197. }
  198. }
  199. } // Repair foundation block check
  200. } // End empty hash check
  201. } // End number of results check
  202. //***********************************************************************************
  203. // How many foundation blocks exist?
  204. $total = mysql_query("SELECT COUNT(*) FROM `transaction_foundation`");
  205. $total = mysql_fetch_array($total);
  206. $foundation_blocks = $total[0];
  207. // How does it compare to the current foundation cycle?
  208. if($foundation_blocks == $current_foundation_block)
  209. {
  210. // No need to run anything
  211. }
  212. else
  213. {
  214. // Check to make sure enough lead time exist in advance to building
  215. // another transaction foundation. (50 blocks) or over 4 hours
  216. // This can be bypassed if the server is building transaction foundations that are older
  217. // than the current foundation.
  218. if($current_generation_block - ($current_foundation_block * 500) > 50 || $current_foundation_block - $foundation_blocks > 1)
  219. {
  220. // Numbers don't match, what do we have?
  221. $sql = "SELECT * FROM `transaction_foundation` ORDER BY `block`";
  222. $sql_result = mysql_query($sql);
  223. for ($i = 0; $i < $current_foundation_block; $i++)
  224. {
  225. $sql_row = mysql_fetch_array($sql_result);
  226. $block = $sql_row["block"];
  227. $hash = $sql_row["hash"];
  228. if($i === intval($block))
  229. {
  230. //Block exist in the correct order
  231. if(empty($hash) == FALSE)
  232. {
  233. // Hash already exist, no need to check again
  234. $rebuild_foundation = FALSE;
  235. }
  236. else
  237. {
  238. // Need to rebuild this transaction foundation
  239. $rebuild_foundation = TRUE;
  240. }
  241. } // End block exist check
  242. else
  243. {
  244. // Need to rebuild this transaction foundation
  245. $rebuild_foundation = TRUE;
  246. }
  247. if($rebuild_foundation == TRUE)
  248. {
  249. // Don't do a history walk if the transclerk is currently working on the
  250. // transaction database
  251. $transclerk_block_check = mysql_result(mysql_query("SELECT * FROM `options` WHERE `field_name` = 'block_check_start' LIMIT 1"),0,"field_data");
  252. if($transclerk_block_check < ($i + 1) * 500 && $transclerk_block_check != "0")
  253. {
  254. // Break out of loop; doing anything until transclerk is finished with this range
  255. break;
  256. }
  257. write_log("Building a New Transaction Foundation for Block #$i", "FO");
  258. // Start the process to rebuild the transaction foundation
  259. // but walk the history of that range first to check for errors.
  260. $foundation_time_start = $i * 500;
  261. $foundation_time_end = ($i * 500) + 500;
  262. $do_history_walk = walkhistory($foundation_time_start, $foundation_time_end);
  263. if($do_history_walk == 0)
  264. {
  265. // History walk checks out, start building the transaction foundation hash
  266. // out of every piece of data in the database
  267. $time1 = transaction_cycle(0 - $current_generation_block + $foundation_time_start);
  268. $time2 = transaction_cycle(0 - $current_generation_block + $foundation_time_end);
  269. $sql = "SELECT * FROM `transaction_history` WHERE `timestamp` >= $time1 AND `timestamp` <= $time2 ORDER BY `timestamp`, `hash`";
  270. $sql_result2 = mysql_query($sql);
  271. $sql_num_results2 = mysql_num_rows($sql_result2);
  272. $hash = $sql_num_results2;
  273. for ($f = 0; $f < $sql_num_results2; $f++)
  274. {
  275. $sql_row2 = mysql_fetch_array($sql_result2);
  276. $hash .= $sql_row2["timestamp"] . $sql_row2["public_key_from"] . $sql_row2["public_key_to"] . $sql_row2["hash"] . $sql_row2["attribute"];
  277. }
  278. $hash = hash('sha256', $hash);
  279. $sql = "INSERT INTO `transaction_foundation` (`block` ,`hash`)VALUES ('$i', '$hash')";
  280. if(mysql_query($sql) == TRUE)
  281. {
  282. // Success
  283. write_log("New Transaction Foundation for Block #$i Complete", "FO");
  284. // Wipe Balance Index table to reset index creation of public key balances
  285. if(mysql_query("TRUNCATE TABLE `balance_index`") == FALSE)
  286. {
  287. write_log("FAILED to Clear Balance Index Table after Transaction Foundation Block #$i was Created", "FO");
  288. }
  289. // Break out of this loop in case there is a lot
  290. // of history to catch up on. We don't want to tie
  291. // up the server with building many transaction foundations
  292. // in a row.
  293. break;
  294. }
  295. }
  296. else
  297. {
  298. write_log("Transaction History Walk FAILED. A Transaction History Check has been scheduled to Examine Transaction Block #$do_history_walk", "FO");
  299. // The history walk failed due to an error somewhere, can't continue.
  300. // Schedule a block check at the location -1 in hopes that it will be cleared up for the next loop
  301. $sql = "UPDATE `options` SET `field_data` = '" . ($do_history_walk - 1) . "' WHERE `field_name` = 'transaction_history_block_check' LIMIT 1";
  302. if(mysql_query($sql) == TRUE)
  303. {
  304. // Break out of this loop to prevent confusing block checks in the database
  305. break;
  306. }
  307. }
  308. } // End rebuild foundation check
  309. } // End for loop
  310. } // End cycle greater than 50 blocks check
  311. } // End foundation block totals vs current check
  312. //***********************************************************************************
  313. } // End generation cycle allowed check
  314. //***********************************************************************************
  315. //***********************************************************************************
  316. // Script finished, set status to 0
  317. $sql = "UPDATE `main_loop_status` SET `field_data` = '0' WHERE `main_loop_status`.`field_name` = 'foundation_heartbeat_active' LIMIT 1";
  318. mysql_query($sql);
  319. // Record when this script finished
  320. $sql = "UPDATE `main_loop_status` SET `field_data` = '" . time() . "' WHERE `main_loop_status`.`field_name` = 'foundation_last_heartbeat' LIMIT 1";
  321. mysql_query($sql);
  322. ?>