require_once ABSPATH . 'includes/errors.php'; $db = new MySQLidb(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME, DB_PORT); do { debug_print("START", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); $urls = array(); // отметимся в БД, что мы живы. upd_deamon_time($db); // проверим, не нужно нам выйти, т.к. обновилась версия скрипта if (check_deamon_restart($db)) { exit; } if (get_my_local_gate_ip($db)) { sleep(5); continue; } $testBlock = new testblock($db, true); // получим id майнеров, которые на нашем уровне $nodes_ids = $testBlock->getOurLevelNodes(); unset($testBlock); if (!$nodes_ids) { debug_print("continue", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); sleep(1); continue; } //print "id майнеров, которые на нашем уровне\n"; debug_print($nodes_ids, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); $add_sql = ''; for ($i = 0; $i < sizeof($nodes_ids); $i++) { $add_sql .= "{$nodes_ids[$i]},"; } $add_sql = substr($add_sql, 0, strlen($add_sql) - 1);
public function __construct($db, $wo_lock = false) { if ($wo_lock) { $this->wo_lock = true; } else { $this->wo_lock = false; } $this->db = $db; /*// получаем наш miner_id и приватный ключ нода $this->my_table = $this->db->query( __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__," SELECT `user_id`, `miner_id` , `private_key` FROM `".DB_PREFIX.MY_PREFIX."my_table` LEFT JOIN `my_node_keys` ON 1=1 WHERE `block_id` = (SELECT max(`block_id`) FROM `".DB_PREFIX.MY_PREFIX."my_node_keys`) ", 'fetch_array' );*/ // print 'testblock my_table'; //print_r($this->my_table); if (!$this->wo_lock) { main_lock(); } // последний успешно записанный блок $this->prev_block = $this->db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\t\tSELECT LOWER(HEX(`hash`)) as `hash`,\n\t\t\t\t\t\t\t LOWER(HEX(`head_hash`)) as `head_hash`,\n\t\t\t\t\t\t\t `block_id`,\n\t\t\t\t\t\t\t `time`,\n\t\t\t\t\t\t\t `level`\n\t\t\t\tFROM `" . DB_PREFIX . "info_block`\n\t\t\t\t", 'fetch_array'); // print 'testblock prev_block'; //print_r($this->prev_block); // общее кол-во майнеров. $max_miner_id = $this->db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\t\tSELECT max(`miner_id`)\n\t\t\t\tFROM `" . DB_PREFIX . "miners`\n\t\t\t\t", 'fetch_one'); $i = 0; do { // если майнера заморозили то у него исчезает miner_id, чтобы не попасть на такой пустой miner_id // нужно пербирать энтропию, пока не дойдем до существующего miner_id if ($i == 0) { $entropy = self::get_entropy($this->prev_block['head_hash']); } else { $block_id = $this->prev_block['block_id'] - $i; if ($block_id < 1) { break; } // bug fixed //if ($this->prev_block['block_id']>10) { $new_head_hash = $this->db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\t\t\t\t\tSELECT LOWER(HEX(`head_hash`)) as `head_hash`\n\t\t\t\t\t\t\tFROM `" . DB_PREFIX . "block_chain`\n\t\t\t\t\t\t\tWHERE `id` = {$block_id}\n\t\t\t\t\t\t\t", 'fetch_one'); $entropy = self::get_entropy($new_head_hash); /*} else { $entropy = $this->db->query( __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__," SELECT `head_hash` FROM `".DB_PREFIX."block_chain` WHERE `id` = {$block_id} ", 'fetch_one' ); }*/ } debug_print('$entropy=' . $entropy, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); debug_print('$max_miner_id=' . $max_miner_id, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); $cur_miner_id = self::get_block_generator_miner_id($max_miner_id, $entropy); // получим ID юзера по его miner_id $this->cur_user_id = $this->db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\t\t\tSELECT `user_id`\n\t\t\t\t\tFROM `" . DB_PREFIX . "miners_data`\n\t\t\t\t\tWHERE `miner_id` = {$cur_miner_id}\n\t\t\t\t\t", 'fetch_one'); $i++; } while (!$this->cur_user_id); $collective = get_my_users_ids($db, true); debug_print('$collective = ' . print_r($collective, true), __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); // в сингл-моде будет только $my_miners_ids[0] $my_miners_ids = get_my_miners_ids($this->db, $collective); debug_print('$my_miners_ids = ' . print_r($my_miners_ids, true), __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); // есть ли кто-то из нашего пула (или сингл-мода), кто находится на 0-м уровне if (in_array($cur_miner_id, $my_miners_ids)) { $this->level = 0; $this->levels_range[0][1] = $this->levels_range[0][0] = 1; $this->miner_id = $cur_miner_id; } else { $this->levels_range = self::get_block_generator_miner_id_range($cur_miner_id, $max_miner_id); debug_print($this->levels_range, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); if ($my_miners_ids) { list($this->miner_id, $this->level) = $this->find_miner_id_level($my_miners_ids, $this->levels_range); } else { $this->level = 'NULL'; // у нас нет уровня, т.к. пуст $my_miners_ids, т.е. на сервере нет майнеров $this->miner_id = 0; } } $this->user_id = $this->db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\t\tSELECT `user_id`\n\t\t\t\tFROM `" . DB_PREFIX . "miners_data`\n\t\t\t\tWHERE `miner_id` = {$this->miner_id}\n\t\t\t\t", 'fetch_one'); debug_print('$this->level =' . $this->level, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); debug_print('$this->miner_id =' . $this->miner_id, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); debug_print('$this->user_id =' . $this->user_id, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); if (!$this->wo_lock) { main_unlock(); } self::$_instance = $this; }
if (!check_input_data($new_testblock['block_id'], 'int')) { die('[error] gate_tetblock.php 1 block_id'); } if (!check_input_data($new_testblock['user_id'], 'int')) { die('[error] gate_tetblock.php user_id'); } if (!check_input_data($new_testblock['time'], 'int')) { die('[error] gate_tetblock.php time'); } if (!check_input_data($new_testblock['mrkl_root'], 'sha256')) { die('[error] gate_tetblock.php mrkl_root'); } //if ( !check_input_data ($new_testblock['signature'] , 'sha256') ) // die('error signature'); main_lock(); $testBlock = new testblock($db, true); if (!isset($testBlock->block_info)) { main_unlock(); die('block_info error'); } // получим id майнеров, которые на нашем уровне $nodes_ids = $testBlock->getOurLevelNodes(); // временно для теста выключим // проверим, верный ли ID блока if ($new_testblock['block_id'] != $testBlock->block_info['block_id'] + 1) { main_unlock(); die("error {$new_testblock['block_id']}!={$testBlock->block_info['block_id']}+1"); } /* * Проблема одновременных попыток локнуть * */
function CheckBlockHeader() { // инфа о предыдущем блоке (т.е. последнем занесенном) if (!$this->prev_block) { // инфа может быть передана прямо в массиве $this->get_prev_block($this->block_data['block_id'] - 1); } //$this->get_info_block(); убрано, т.к. CheckBlockHeader используется и при сборе новых блоков при вилке // для локальных тестов if ($this->prev_block['block_id'] == 1) { $ini_array = parse_ini_file(ABSPATH . "config.ini", true); if (isset($ini_array['local']['start_block_id'])) { $this->prev_block['block_id'] = $ini_array['local']['start_block_id']; } } debug_print("this->prev_block: " . print_r_hex($this->prev_block), __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); debug_print("this->block_data: " . print_r_hex($this->block_data), __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); // меркель рут нужен для проверки подписи блока, а также проверки лимитов MAX_TX_SIZE и MAX_TX_COUNT if ($this->block_data['block_id'] == 1) { $this->global_variables['max_tx_size'] = 1024 * 1024; $first = true; } else { $first = false; } $this->mrkl_root = self::getMrklroot($this->binary_data, $this->global_variables, $first); // проверим время if (!check_input_data($this->block_data['time'], 'int')) { return 'error time'; } // проверим уровень if (!check_input_data($this->block_data['level'], 'level')) { return 'error level'; } // получим значения для сна $sleep_data = $this->db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\t\t\tSELECT `value`\n\t\t\t\t\tFROM `" . DB_PREFIX . "variables`\n\t\t\t\t\tWHERE `name` = 'sleep'\n\t\t\t\t\t", 'fetch_one'); $sleep_data = json_decode($sleep_data, true); debug_print("sleep_data:", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); //print_R($sleep_data); // узнаем время, которые было затрачено в ожидании is_ready предыдущим блоком $is_ready_sleep = testblock::get_is_ready_sleep($this->prev_block['level'], $sleep_data['is_ready']); // сколько сек должен ждать нод, перед тем, как начать генерить блок, если нашел себя в одном из уровней. $generator_sleep = testblock::get_generator_sleep($this->block_data['level'], $sleep_data['generator']); // сумма is_ready всех предыдущих уровней, которые не успели сгенерить блок $is_ready_sleep2 = testblock::get_is_ready_sleep_sum($this->block_data['level'], $sleep_data['is_ready']); debug_print("is_ready_sleep={$is_ready_sleep}\ngenerator_sleep={$generator_sleep}\nis_ready_sleep2={$is_ready_sleep2}", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); debug_print('prev_block:' . print_r_hex($this->prev_block), __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); debug_print('block_data:' . print_r_hex($this->block_data), __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); // не слишком ли рано прислан этот блок. допустима погрешность = error_time if (!$first) { if ($this->prev_block['time'] + $is_ready_sleep + $generator_sleep + $is_ready_sleep2 - $this->block_data['time'] > $this->global_variables['error_time']) { return "error block time {$this->prev_block['time']} + {$is_ready_sleep} + {$generator_sleep} + {$is_ready_sleep2} - {$this->block_data['time']} > {$this->global_variables['error_time']}\n"; } } // исключим тех, кто сгенерил блок с бегущими часами if ($this->block_data['time'] > time()) { return "error block time"; } // проверим ID блока if (!check_input_data($this->block_data['block_id'], 'int')) { return 'block_id'; } // проверим, верный ли ID блока if (!$first) { if ($this->block_data['block_id'] != $this->prev_block['block_id'] + 1) { return "error block_id ({$this->block_data['block_id']}!=" . ($this->prev_block['block_id'] + 1) . ")"; } } // проверим, есть ли такой майнер и заодно получим public_key // ================================== my_table заменить =============================================== $this->node_public_key = $this->db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\t\t\tSELECT `node_public_key`\n\t\t\t\t\tFROM `" . DB_PREFIX . "miners_data`\n\t\t\t\t\tWHERE `user_id` = {$this->block_data['user_id']}\n\t\t\t\t\tLIMIT 1\n\t\t\t\t\t", 'fetch_one'); if (!$first) { if (!$this->node_public_key) { return 'user_id'; } } // SIGN от 128 байта до 512 байт. Подпись от TYPE, BLOCK_ID, PREV_BLOCK_HASH, TIME, USER_ID, LEVEL, MRKL_ROOT $for_sign = "0,{$this->block_data['block_id']},{$this->prev_block['hash']},{$this->block_data['time']},{$this->block_data['user_id']},{$this->block_data['level']},{$this->mrkl_root}"; debug_print("checkSign", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); // проверяем подпись if (!$first) { $error = self::checkSign($this->node_public_key, $for_sign, $this->block_data['sign'], true); if ($error) { return $error; } } }