コード例 #1
0
ファイル: blocks_collection.php プロジェクト: scuba323/dcoin
function downloadFile($url, $path)
{
    global $db;
    $newfname = $path;
    $file = fopen($url, "rb");
    if ($file) {
        $newf = fopen($newfname, "wb");
        if ($newf) {
            while (!feof($file)) {
                fwrite($newf, fread($file, 1024 * 8), 1024 * 8);
                upd_deamon_time($db);
                if (check_deamon_restart($db)) {
                    main_unlock();
                    exit;
                }
            }
        }
    }
    if ($file) {
        fclose($file);
    }
    if ($newf) {
        fclose($newf);
    }
}
コード例 #2
0
 * В 02:07 testblock_is_ready.php на 56 и 57 начнет отправлять нодам на других уровнях блок 1001
 * */
// работаем в бесконечном цикле со слипом 0,1 сек
do {
    debug_print("=================================>>> START", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__);
    main_lock();
    //main_lock();
    $block_id = get_block_id($db);
    $new_block_id = $block_id + 1;
    // если в testblock уже есть такой блок, то пропускаем
    $testblock_id = get_testblock_id($db);
    debug_print("new_block_id={$new_block_id} // testblock_id={$testblock_id}", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__);
    // отметимся в БД, что мы живы.
    upd_deamon_time($db);
    // проверим, не нужно ли нам выйти, т.к. обновилась версия скрипта
    if (check_deamon_restart($db)) {
        main_unlock();
        exit;
    }
    if (get_my_local_gate_ip($db)) {
        main_unlock();
        sleep(5);
        continue;
    }
    //main_unlock();
    if ($testblock_id == $new_block_id) {
        debug_print("block_id+1 == testblock_id", __FILE__, __LINE__, __FUNCTION__, __CLASS__, __METHOD__);
        main_unlock();
        //ob_save();
        sleep(1);
        continue;
コード例 #3
0
ファイル: fns-main.php プロジェクト: scuba323/dcoin
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__);
}