$db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\t\t\t\tUPDATE `" . DB_PREFIX . "my_table`\n\t\t\t\t\t\tSET `user_id`={$user_id},\n\t\t\t\t\t\t\t `status` = 'user'\n\t\t\t\t\t\t"); } else { $error = 1; } } else { $error = 1; } } else { $ini_array = parse_ini_file(ABSPATH . "config.ini", true); if ($ini_array['main']['sign_hash'] == 'ip') { $hash = md5($_SERVER['REMOTE_ADDR']); } else { $hash = md5($_SERVER['HTTP_USER_AGENT'] . $_SERVER['REMOTE_ADDR']); } $for_sign = $db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\t\t\tSELECT `data`\n\t\t\t\t\tFROM `" . DB_PREFIX . "authorization`\n\t\t\t\t\tWHERE `hash` = 0x{$hash}\n\t\t\t\t\t", 'fetch_one'); $error = ParseData::checkSign($public_key, $for_sign, $sign, true); } if ($error) { $result = 0; } else { session_start(); define('MY_PREFIX', ''); $my_user_id = get_my_user_id($db); unset($_SESSION['restricted']); // убираем ограниченный режим $_SESSION['user_id'] = $my_user_id; if (!$_SESSION['user_id']) { $_SESSION['user_id'] = 'wait'; } else { $_SESSION['public_key'] = get_user_public_key2($my_user_id); }
debug_print($variables, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); $mrkl_root = ParseData::getMrklroot($binary_block, $variables, $first); debug_print('$mrkl_root=' . $mrkl_root, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); if (substr($mrkl_root, 0, 7) == '[error]') { nodes_ban($db, $max_block_id_user_id, __FILE__ . ', ' . __LINE__ . ', ' . __FUNCTION__ . ', ' . __CLASS__ . ', ' . __METHOD__); main_unlock(); continue 2; } // публичный ключ того, кто этот блок сгенерил $node_public_key = $db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\t\tSELECT `node_public_key`\n\t\t\t\tFROM `" . DB_PREFIX . "miners_data`\n\t\t\t\tWHERE `user_id` = {$block_data['user_id']}\n\t\t\t\t", 'fetch_one'); //print '$node_public_key='.$node_public_key."\n"; // SIGN от 128 байта до 512 байт. Подпись от TYPE, BLOCK_ID, PREV_BLOCK_HASH, TIME, USER_ID, LEVEL, MRKL_ROOT $for_sign = "0,{$block_data['block_id']},{$prev_block_hash},{$block_data['time']},{$block_data['user_id']},{$block_data['level']},{$mrkl_root}"; // проверяем подпись if (!$first) { $error = ParseData::checkSign($node_public_key, $for_sign, $block_data['sign'], true); } // качаем предыдущие блоки до тех пор, пока отличается хэш предудущего. // другими словами, пока подпись с $prev_block_hash будет неверной, т.е. пока что-то есть в $error if ($error) { debug_print("error block_collection checkSign={$error}", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); //main_unlock(); if ($block_id < 1) { debug_print('[error] $block_id < 1 continue 2', __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); main_unlock(); continue 2; } // нужно привести данные в нашей БД в соответствие с данными у того, у кого качаем более свежий блок $LOG_MARKER = "download prev blocks get_old_blocks({$block_data['user_id']}, " . ($block_id - 1) . ", {$max_block_id_host}, {$max_block_id_user_id})"; $result = get_old_blocks($block_data['user_id'], $block_id - 1, $max_block_id_host, $max_block_id_user_id, $get_block_script_name, $add_node_host); debug_print($result, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__);
function get_blocks($block_id, $host, $user_id, $rollback_blocks, $get_block_script_name = '', $add_node_host = '') { global $db, $variables; debug_print('$block_id=' . $block_id, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); $blocks = array(); $count = 0; do { // отметимся в БД, что мы живы. upd_deamon_time($db); // отметимся, чтобы не спровоцировать очистку таблиц upd_main_lock($db); // проверим, не нужно нам выйти, т.к. обновилась версия скрипта if (check_deamon_restart($db)) { main_unlock(); exit; } if ($block_id < 2) { return '[error] $block_id < 2'; } // если превысили лимит кол-ва полученных от нода блоков if ($count > $variables[$rollback_blocks]) { //delete_queue_block(); clear_tmp($blocks); return 'error $count > $variables[$rollback_blocks] (' . $count . '>' . $variables[$rollback_blocks] . ') [' . $rollback_blocks . ']'; } if (!$host) { $hosts = $db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\t\tSELECT `host`\n\t\t\t\tFROM `" . DB_PREFIX . "miners_data`\n\t\t\t\tWHERE `user_id` = {$user_id}\n\t\t\t\t", 'fetch_array'); $rk = array_rand($hosts, 1); $host = $hosts[$rk]; } if (!$get_block_script_name) { $url = "{$host}/get_block.php?id={$block_id}"; } else { $url = "{$host}/{$get_block_script_name}?id={$block_id}{$add_node_host}"; } debug_print($url, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); $ch = curl_init(); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); curl_setopt($ch, CURLOPT_TIMEOUT, 60); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $binary_block = curl_exec($ch); curl_close($ch); debug_print('$binary_block=' . $binary_block, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); $binary_block_full = $binary_block; if (!$binary_block) { debug_print('continue 2 !$binary_block', __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); clear_tmp($blocks); return 'error !$binary_block'; } ParseData::string_shift($binary_block, 1); // уберем 1-й байт - тип (блок/тр-я) // распарсим заголовок блока $block_data = parse_block_header($binary_block); debug_print("block_data=" . print_r_hex($block_data), __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); // если существуют глючная цепочка, тот тут мы её проигнорируем $bad_blocks = $db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\t\tSELECT `bad_blocks`\n\t\t\t\tFROM `" . DB_PREFIX . "config`\n\t\t\t\t", 'fetch_one'); $bad_blocks = json_decode($bad_blocks, true); debug_print('$bad_blocks=' . print_r_hex($bad_blocks), __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); if (@$bad_blocks[$block_data['block_id']] == bin2hex($block_data['sign'])) { clear_tmp($blocks); return "bad_block = {$block_data['block_id']}=>{$bad_blocks[$block_data['block_id']]}"; } if ($block_data['block_id'] != $block_id) { debug_print("bad block_data['block_id']", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); clear_tmp($blocks); return "bad block_data['block_id']"; } // размер блока не может быть более чем max_block_size if (strlen($binary_block) > $variables['max_block_size']) { //delete_queue_block(); clear_tmp($blocks); return 'error strlen($binary_block) > $variables[max_block_size] '; } // нам нужен хэш предыдущего блока, чтобы найти, где началась вилка $prev_block_hash = bin2hex($db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\t\tSELECT `hash`\n\t\t\t\tFROM `" . DB_PREFIX . "block_chain`\n\t\t\t\tWHERE `id` = " . ($block_id - 1) . "\n\t\t\t\t", 'fetch_one')); //debug_print($db->printsql()."\nprev_block_hash={$prev_block_hash}", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); // нам нужен меркель-рут текущего блока $mrkl_root = ParseData::getMrklroot($binary_block, $variables); if (substr($mrkl_root, 0, 7) == '[error]') { //delete_queue_block(); clear_tmp($blocks); return '$mrkl_root error'; } // публичный ключ того, кто этот блок сгенерил $node_public_key = $db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\t\tSELECT `node_public_key`\n\t\t\t\tFROM `" . DB_PREFIX . "miners_data`\n\t\t\t\tWHERE `user_id` = {$block_data['user_id']}\n\t\t\t\t", 'fetch_one'); // SIGN от 128 байта до 512 байт. Подпись от TYPE, BLOCK_ID, PREV_BLOCK_HASH, TIME, USER_ID, LEVEL, MRKL_ROOT $for_sign = "0,{$block_data['block_id']},{$prev_block_hash},{$block_data['time']},{$block_data['user_id']},{$block_data['level']},{$mrkl_root}"; debug_print("for_sign={$for_sign}", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); // проверяем подпись $error = ParseData::checkSign($node_public_key, $for_sign, $block_data['sign'], true); if ($error) { debug_print("error={$error}", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); } // сам блок сохраняем в файл, чтобы не нагружать память $tmp_file_name = tempnam(sys_get_temp_dir(), 'FCB'); $blocks[$block_id] = $tmp_file_name; file_put_contents($tmp_file_name, $binary_block_full); $block_id--; $count++; // качаем предыдущие блоки до тех пор, пока отличается хэш предудущего. // другими словами, пока подпись с $prev_block_hash будет неверной, т.е. пока что-то есть в $error if (!$error) { debug_print("===========Вилка найдена\nСошлись на блоке {$block_data['block_id']}", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); break; } } while (true); // чтобы брать блоки по порядку ksort($blocks); debug_print("blocks:" . print_r_hex($blocks), __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); // запрещаем любые добавления данных из блоков/тр-ий //main_lock(); // получим наши транзакции в 1 бинарнике, просто для удобства $transactions = ''; $res = $db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\tSELECT `data`\n\t\t\tFROM `" . DB_PREFIX . "transactions`\n\t\t\tWHERE `verified` = 1 AND\n\t\t\t\t\t\t `used` = 0\n\t\t\t"); while ($row = $db->fetchArray($res)) { debug_print($row, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); $transactions .= ParseData::encode_length_plus_data($row['data']); } if ($transactions) { // отмечаем, что эти тр-ии теперь нужно проверять по новой $db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\t\tUPDATE `" . DB_PREFIX . "transactions`\n\t\t\t\tSET `verified` = 0\n\t\t\t\tWHERE `verified` = 1 AND\n\t\t\t\t\t\t\t `used` = 0\n\t\t\t\t"); // откатываем по фронту все свежие тр-ии $parsedata = new ParseData($transactions, $db); $parsedata->ParseDataRollbackFront(); unset($parsedata); } // теперь откатим и transactions_testblock debug_print('rollback_transactions_testblock start', __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); rollback_transactions_testblock($db, true); $db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\tTRUNCATE TABLE `" . DB_PREFIX . "testblock`\n\t\t\t"); // откатываем наши блоки до начала вилки $res = $db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\tSELECT *\n\t\t\tFROM `" . DB_PREFIX . "block_chain`\n\t\t\tWHERE `id` > {$block_id}\n\t\t\tORDER BY `id` DESC\n\t\t\t"); while ($row = $db->fetchArray($res)) { debug_print($row, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); $LOG_MARKER = "откатываем наши блоки до начала вилки\nParseDataRollback start\nblock_id>{$block_id}"; $parsedata = new ParseData($row['data'], $db); $parsedata->ParseDataRollback(); unset($parsedata); } debug_print($blocks, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); $prev_block = array(); // проходимся по новым блокам foreach ($blocks as $int_block_id => $tmp_file_name) { debug_print("# # # проходимся по новым блокам\nblock_id={$int_block_id}\ntmp_file_name={$tmp_file_name}", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); // проверяем и заносим данные $binary_block = file_get_contents($tmp_file_name); debug_print('$binary_block = ' . $binary_block, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); debug_print('$binary_block hex = ' . bin2hex($binary_block), __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); $parsedata = new ParseData($binary_block, $db); // передаем инфу о предыдущем блоке, т.к. это новые блоки, то инфа о предыдущих блоках в block_chain будет всё еще старая, т.к. обновление block_chain идет ниже if (isset($prev_block[$int_block_id - 1])) { $parsedata->prev_block['hash'] = $prev_block[$int_block_id - 1]['hash']; $parsedata->prev_block['head_hash'] = $prev_block[$int_block_id - 1]['head_hash']; $parsedata->prev_block['time'] = $prev_block[$int_block_id - 1]['time']; $parsedata->prev_block['level'] = $prev_block[$int_block_id - 1]['level']; $parsedata->prev_block['block_id'] = $prev_block[$int_block_id - 1]['block_id']; } // если вернулась ошибка, значит переданный блок уже откатился // info_block и config.my_block_id обновляются только если ошибки не было $error = $parsedata->ParseDataFull(); // для последующей обработки получим хэши и time if (!$error) { $prev_block[$int_block_id] = $parsedata->GetBlockInfo(); } unset($parsedata); // если есть ошибка, то откатываем все предыдущие блоки из новой цепочки if ($error) { debug_print("[error]={$error}\nесть ошибка, откатываем все предыдущие блоки из новой цепочки\n", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); // баним на 1 час хост, который дал нам ложную цепочку; nodes_ban($db, $user_id, $error . "\n" . __FILE__ . ', ' . __LINE__ . ', ' . __FUNCTION__ . ', ' . __CLASS__ . ', ' . __METHOD__); // обязательно проходимся по блокам в обратном порядке krsort($blocks); foreach ($blocks as $int2_block_id => $tmp_file_name) { @($LOG_MARKER .= "int2_block_id={$int2_block_id}"); debug_print("int2_block_id={$int2_block_id}", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); if ($int2_block_id >= $int_block_id) { continue; } $binary_block = file_get_contents($tmp_file_name); $parsedata = new ParseData($binary_block, $db); $parsedata->ParseDataRollback(); unset($parsedata); debug_print("[{$int2_block_id}] ParseDataRollback ok\n", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); } // заносим наши данные из block_chain, которые были ранее debug_print("заносим наши данные из block_chain, которые были ранее", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); $res = $db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\t\t\tSELECT *\n\t\t\t\t\tFROM `" . DB_PREFIX . "block_chain`\n\t\t\t\t\tWHERE `id` > {$block_id}\n\t\t\t\t\tORDER BY `id` ASC\n\t\t\t"); while ($row = $db->fetchArray($res)) { debug_print('$block_id=' . $block_id, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); debug_print("[{$int_block_id}] ParseDataFull start\n", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); debug_print($row, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); $parsedata = new ParseData($row['data'], $db); $parsedata->ParseDataFull(); unset($parsedata); } // т.к. в предыдущем запросе к block_chain могло не быть данных, т.к. $block_id больше чем наш самый большой id в block_chain // то значит info_block мог не обновится и остаться от занесения новых блоков, что приведет к пропуску блока в block_chain $last_my_block = $db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\t\t\tSELECT *\n\t\t\t\t\tFROM `" . DB_PREFIX . "block_chain`\n\t\t\t\t\tORDER BY `id` DESC\n\t\t\t\t\tLIMIT 1\n\t\t\t\t\t", 'fetch_array'); ParseData::string_shift($last_my_block['data'], 1); // уберем 1-й байт - тип (блок/тр-я) $last_my_block_data = parse_block_header($last_my_block['data']); $db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\t\t\tUPDATE `" . DB_PREFIX . "info_block`\n\t\t\t\t\tSET `hash` = 0x" . bin2hex($last_my_block['hash']) . ",\n\t\t\t\t\t\t\t`head_hash` = 0x" . bin2hex($last_my_block['head_hash']) . ",\n\t\t\t\t\t\t\t`block_id`= {$last_my_block_data['block_id']},\n\t\t\t\t\t\t\t`time`= {$last_my_block_data['time']},\n\t\t\t\t\t\t\t`level`= {$last_my_block_data['level']},\n\t\t\t\t\t\t\t`sent` = 0\n\t\t\t\t\t"); debug_print($db->printsql(), __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); $db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\t\t\tUPDATE `" . DB_PREFIX . "config`\n\t\t\t\t\tSET `my_block_id` = {$last_my_block_data['block_id']}\n\t\t\t\t\tWHERE `my_block_id` < {$last_my_block_data['block_id']}\n\t\t\t\t\t"); clear_tmp($blocks); return 'get_block error ' . $error; // переходим к следующему блоку в queue_blocks } } debug_print("# # удаляем блоки из block_chain и заносим новые", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); // если всё занеслось без ошибок, то удаляем блоки из block_chain и заносим новые $db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\tDELETE\n\t\t\tFROM `" . DB_PREFIX . "block_chain`\n\t\t\tWHERE `id` > {$block_id}\n\t\t\t"); debug_print($db->getAffectedRows(), __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); debug_print($db->printsql(), __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); debug_print("blocks:" . print_r_hex($blocks), __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); debug_print($prev_block, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); // для поиска бага $max_block_id = $db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\tSELECT `id`\n\t\t\tFROM `" . DB_PREFIX . "block_chain`\n\t\t\tORDER BY `id` DESC\n\t\t\tLIMIT 1\n\t\t\t", 'fetch_one'); debug_print('$max_block_id=' . $max_block_id, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); // проходимся по новым блокам foreach ($blocks as $block_id => $tmp_file_name) { $block_hex = bin2hex(file_get_contents($tmp_file_name)); // пишем в цепочку блоков $file = save_tmp_644('FBC', "{$block_id}\t{$prev_block[$block_id]['hash']}\t{$prev_block[$block_id]['head_hash']}\t{$block_hex}"); debug_print("{$block_id} ==> LOAD DATA LOCAL INFILE '{$file}' IGNORE INTO TABLE `" . DB_PREFIX . "block_chain`", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); $db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\t\tUPDATE `" . DB_PREFIX . "info_block`\n\t\t\t\tSET `hash` = 0x{$prev_block[$block_id]['hash']},\n\t\t\t\t\t\t`head_hash` = 0x{$prev_block[$block_id]['head_hash']},\n\t\t\t\t\t\t`block_id`= {$prev_block[$block_id]['block_id']},\n\t\t\t\t\t\t`time`= {$prev_block[$block_id]['time']},\n\t\t\t\t\t\t`level`= {$prev_block[$block_id]['level']},\n\t\t\t\t\t\t`sent` = 0\n\t\t\t\t"); debug_print($db->printsql(), __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); // т.к. эти данные создали мы сами, то пишем их сразу в таблицу проверенных данных, которые будут отправлены другим нодам $db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\t\tLOAD DATA LOCAL INFILE '{$file}' IGNORE INTO TABLE `" . DB_PREFIX . "block_chain`\n\t\t\t\tFIELDS TERMINATED BY '\t'\n\t\t\t\t(`id`, @hash, @head_hash, @data)\n\t\t\t\tSET `hash` = UNHEX(@hash),\n\t\t\t\t\t `head_hash` = UNHEX(@head_hash),\n\t\t\t\t\t `data` = UNHEX(@data)\n\t\t\t\t"); debug_print($db->getAffectedRows(), __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); debug_print($db->printsql(), __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); unlink($file); unlink($tmp_file_name); // для поиска бага $max_block_id = $db->query(__FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__, "\n\t\t\tSELECT `id`\n\t\t\tFROM `" . DB_PREFIX . "block_chain`\n\t\t\tORDER BY `id` DESC\n\t\t\tLIMIT 1\n\t\t\t", 'fetch_one'); debug_print('$max_block_id=' . $max_block_id, __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); } debug_print("-------------------------HAPPY END ---------------------------", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__); }