From b13e7f284b909df8ca54fe93c231910a130f9f3e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 16 Oct 2015 19:45:35 +0100 Subject: [PATCH] blockchain_export can now export to a blocks.dat format Also make the number of blocks endian independant, and add support for testnet --- src/blockchain_utilities/CMakeLists.txt | 4 + .../blockchain_export.cpp | 17 +- .../blockchain_utilities.h | 51 ++++++ src/blockchain_utilities/blocksdat_file.cpp | 170 ++++++++++++++++++ src/blockchain_utilities/blocksdat_file.h | 90 ++++++++++ src/blockchain_utilities/bootstrap_file.h | 19 +- src/blocks/CMakeLists.txt | 6 +- src/blocks/blockexports.c | 34 +++- src/blocks/blocks.h | 4 +- src/blocks/testnet_blocks.dat | 0 src/cryptonote_core/blockchain.cpp | 11 +- 11 files changed, 368 insertions(+), 38 deletions(-) create mode 100644 src/blockchain_utilities/blockchain_utilities.h create mode 100644 src/blockchain_utilities/blocksdat_file.cpp create mode 100644 src/blockchain_utilities/blocksdat_file.h create mode 100644 src/blocks/testnet_blocks.dat diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index 5be37c450..7be9d95f7 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -38,11 +38,13 @@ bitmonero_private_headers(blockchain_converter set(blockchain_import_sources blockchain_import.cpp bootstrap_file.cpp + blocksdat_file.cpp ) set(blockchain_import_private_headers fake_core.h bootstrap_file.h + blocksdat_file.h bootstrap_serialization.h ) @@ -52,10 +54,12 @@ bitmonero_private_headers(blockchain_import set(blockchain_export_sources blockchain_export.cpp bootstrap_file.cpp + blocksdat_file.cpp ) set(blockchain_export_private_headers bootstrap_file.h + blocksdat_file.h bootstrap_serialization.h ) diff --git a/src/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp index 8db8b7cdb..4a96bad6b 100644 --- a/src/blockchain_utilities/blockchain_export.cpp +++ b/src/blockchain_utilities/blockchain_export.cpp @@ -27,6 +27,7 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "bootstrap_file.h" +#include "blocksdat_file.h" #include "common/command_line.h" #include "version.h" @@ -39,6 +40,7 @@ int main(int argc, char* argv[]) { uint32_t log_level = 0; uint64_t block_stop = 0; + bool blocks_dat = false; boost::filesystem::path default_data_path {tools::get_default_data_dir()}; boost::filesystem::path default_testnet_data_path {default_data_path / "testnet"}; @@ -54,6 +56,7 @@ int main(int argc, char* argv[]) , "Run on testnet." , false }; + const command_line::arg_descriptor arg_blocks_dat = {"blocksdat", "Output in blocks.dat format", blocks_dat}; command_line::add_arg(desc_cmd_sett, command_line::arg_data_dir, default_data_path.string()); @@ -62,6 +65,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_cmd_sett, arg_testnet_on); command_line::add_arg(desc_cmd_sett, arg_log_level); command_line::add_arg(desc_cmd_sett, arg_block_stop); + command_line::add_arg(desc_cmd_sett, arg_blocks_dat); command_line::add_arg(desc_cmd_only, command_line::arg_help); @@ -94,6 +98,7 @@ int main(int argc, char* argv[]) LOG_PRINT_L0("Setting log level = " << log_level); bool opt_testnet = command_line::get_arg(vm, arg_testnet_on); + bool opt_blocks_dat = command_line::get_arg(vm, arg_blocks_dat); std::string m_config_folder; @@ -155,8 +160,16 @@ int main(int argc, char* argv[]) LOG_PRINT_L0("Source blockchain storage initialized OK"); LOG_PRINT_L0("Exporting blockchain raw data..."); - BootstrapFile bootstrap; - r = bootstrap.store_blockchain_raw(core_storage, NULL, output_file_path, block_stop); + if (opt_blocks_dat) + { + BlocksdatFile blocksdat; + r = blocksdat.store_blockchain_raw(core_storage, NULL, output_file_path, block_stop); + } + else + { + BootstrapFile bootstrap; + r = bootstrap.store_blockchain_raw(core_storage, NULL, output_file_path, block_stop); + } CHECK_AND_ASSERT_MES(r, false, "Failed to export blockchain raw data"); LOG_PRINT_L0("Blockchain raw data exported OK"); } diff --git a/src/blockchain_utilities/blockchain_utilities.h b/src/blockchain_utilities/blockchain_utilities.h new file mode 100644 index 000000000..ff57a9320 --- /dev/null +++ b/src/blockchain_utilities/blockchain_utilities.h @@ -0,0 +1,51 @@ +// Copyright (c) 2014-2015, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "version.h" + + +// CONFIG: choose one of the three #define's +// +// DB_MEMORY is a sensible default for users migrating to LMDB, as it allows +// the exporter to use the in-memory blockchain while the other binaries +// work with LMDB, without recompiling anything. +// +#define SOURCE_DB DB_MEMORY +//#define SOURCE_DB DB_LMDB +// to use global compile-time setting (DB_MEMORY or DB_LMDB): +// #define SOURCE_DB BLOCKCHAIN_DB + + +// bounds checking is done before writing to buffer, but buffer size +// should be a sensible maximum +#define BUFFER_SIZE 1000000 +#define NUM_BLOCKS_PER_CHUNK 1 +#define BLOCKCHAIN_RAW "blockchain.raw" + diff --git a/src/blockchain_utilities/blocksdat_file.cpp b/src/blockchain_utilities/blocksdat_file.cpp new file mode 100644 index 000000000..f7de2a480 --- /dev/null +++ b/src/blockchain_utilities/blocksdat_file.cpp @@ -0,0 +1,170 @@ +// Copyright (c) 2014-2015, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "blocksdat_file.h" + + +namespace po = boost::program_options; + +using namespace cryptonote; +using namespace epee; + +namespace +{ + std::string refresh_string = "\r \r"; +} + + + +bool BlocksdatFile::open_writer(const boost::filesystem::path& file_path, uint64_t block_stop) +{ + const boost::filesystem::path dir_path = file_path.parent_path(); + if (!dir_path.empty()) + { + if (boost::filesystem::exists(dir_path)) + { + if (!boost::filesystem::is_directory(dir_path)) + { + LOG_PRINT_RED_L0("export directory path is a file: " << dir_path); + return false; + } + } + else + { + if (!boost::filesystem::create_directory(dir_path)) + { + LOG_PRINT_RED_L0("Failed to create directory " << dir_path); + return false; + } + } + } + + m_raw_data_file = new std::ofstream(); + + uint64_t num_blocks = 0; + + LOG_PRINT_L0("creating file"); + + m_raw_data_file->open(file_path.string(), std::ios_base::binary | std::ios_base::out | std::ios::trunc); + if (m_raw_data_file->fail()) + return false; + + initialize_file(block_stop); + + return true; +} + + +bool BlocksdatFile::initialize_file(uint64_t block_stop) +{ + const uint32_t nblocks = block_stop + 1; + unsigned char nblocksc[4]; + + nblocksc[0] = nblocks & 0xff; + nblocksc[1] = (nblocks >> 8) & 0xff; + nblocksc[2] = (nblocks >> 16) & 0xff; + nblocksc[3] = (nblocks >> 24) & 0xff; + + // 4 bytes little endian + *m_raw_data_file << nblocksc[0]; + *m_raw_data_file << nblocksc[1]; + *m_raw_data_file << nblocksc[2]; + *m_raw_data_file << nblocksc[3]; + + return true; +} + +void BlocksdatFile::write_block(const crypto::hash& block_hash) +{ + const std::string data(block_hash.data, sizeof(block_hash)); + *m_raw_data_file << data; +} + +bool BlocksdatFile::close() +{ + if (m_raw_data_file->fail()) + return false; + + m_raw_data_file->flush(); + delete m_raw_data_file; + return true; +} + + +#if SOURCE_DB == DB_MEMORY +bool BlocksdatFile::store_blockchain_raw(blockchain_storage* _blockchain_storage, tx_memory_pool* _tx_pool, boost::filesystem::path& output_file, uint64_t requested_block_stop) +#else +bool BlocksdatFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_memory_pool* _tx_pool, boost::filesystem::path& output_file, uint64_t requested_block_stop) +#endif +{ + uint64_t num_blocks_written = 0; + m_blockchain_storage = _blockchain_storage; + uint64_t progress_interval = 100; + block b; + + uint64_t block_start = 0; + uint64_t block_stop = 0; + LOG_PRINT_L0("source blockchain height: " << m_blockchain_storage->get_current_blockchain_height()-1); + if ((requested_block_stop > 0) && (requested_block_stop < m_blockchain_storage->get_current_blockchain_height())) + { + LOG_PRINT_L0("Using requested block height: " << requested_block_stop); + block_stop = requested_block_stop; + } + else + { + block_stop = m_blockchain_storage->get_current_blockchain_height() - 1; + LOG_PRINT_L0("Using block height of source blockchain: " << block_stop); + } + LOG_PRINT_L0("Storing blocks raw data..."); + if (!BlocksdatFile::open_writer(output_file, block_stop)) + { + LOG_PRINT_RED_L0("failed to open raw file for write"); + return false; + } + for (m_cur_height = block_start; m_cur_height <= block_stop; ++m_cur_height) + { + // this method's height refers to 0-based height (genesis block = height 0) + crypto::hash hash = m_blockchain_storage->get_block_id_by_height(m_cur_height); + write_block(hash); + if (m_cur_height % NUM_BLOCKS_PER_CHUNK == 0) { + num_blocks_written += NUM_BLOCKS_PER_CHUNK; + } + if (m_cur_height % progress_interval == 0) { + std::cout << refresh_string; + std::cout << "block " << m_cur_height << "/" << block_stop << std::flush; + } + } + // print message for last block, which may not have been printed yet due to progress_interval + std::cout << refresh_string; + std::cout << "block " << m_cur_height-1 << "/" << block_stop << ENDL; + + LOG_PRINT_L0("Number of blocks exported: " << num_blocks_written); + + return BlocksdatFile::close(); +} + diff --git a/src/blockchain_utilities/blocksdat_file.h b/src/blockchain_utilities/blocksdat_file.h new file mode 100644 index 000000000..eefef6fa6 --- /dev/null +++ b/src/blockchain_utilities/blocksdat_file.h @@ -0,0 +1,90 @@ +// Copyright (c) 2014-2015, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include +#include +#include + +#include + +#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_core/cryptonote_boost_serialization.h" +#include "cryptonote_core/blockchain_storage.h" +#include "cryptonote_core/blockchain.h" +#include "blockchain_db/blockchain_db.h" +#include "blockchain_db/lmdb/db_lmdb.h" + +#include +#include +#include +#include +#include + +#include "common/command_line.h" +#include "version.h" + +#include "blockchain_utilities.h" + + +using namespace cryptonote; + + +class BlocksdatFile +{ +public: + +#if SOURCE_DB == DB_MEMORY + bool store_blockchain_raw(cryptonote::blockchain_storage* cs, cryptonote::tx_memory_pool* txp, + boost::filesystem::path& output_file, uint64_t use_block_height=0); +#else + bool store_blockchain_raw(cryptonote::Blockchain* cs, cryptonote::tx_memory_pool* txp, + boost::filesystem::path& output_file, uint64_t use_block_height=0); +#endif + +protected: + +#if SOURCE_DB == DB_MEMORY + blockchain_storage* m_blockchain_storage; +#else + Blockchain* m_blockchain_storage; +#endif + + std::ofstream * m_raw_data_file; + + // open export file for write + bool open_writer(const boost::filesystem::path& file_path, uint64_t block_stop); + bool initialize_file(uint64_t block_stop); + bool close(); + void write_block(const crypto::hash &block_hash); + +private: + + uint64_t m_cur_height; // tracks current height during export +}; diff --git a/src/blockchain_utilities/bootstrap_file.h b/src/blockchain_utilities/bootstrap_file.h index fcf89d1ac..6f8df18dd 100644 --- a/src/blockchain_utilities/bootstrap_file.h +++ b/src/blockchain_utilities/bootstrap_file.h @@ -49,24 +49,7 @@ #include "common/command_line.h" #include "version.h" - -// CONFIG: choose one of the three #define's -// -// DB_MEMORY is a sensible default for users migrating to LMDB, as it allows -// the exporter to use the in-memory blockchain while the other binaries -// work with LMDB, without recompiling anything. -// -#define SOURCE_DB DB_MEMORY -// #define SOURCE_DB DB_LMDB -// to use global compile-time setting (DB_MEMORY or DB_LMDB): -// #define SOURCE_DB BLOCKCHAIN_DB - - -// bounds checking is done before writing to buffer, but buffer size -// should be a sensible maximum -#define BUFFER_SIZE 1000000 -#define NUM_BLOCKS_PER_CHUNK 1 -#define BLOCKCHAIN_RAW "blockchain.raw" +#include "blockchain_utilities.h" using namespace cryptonote; diff --git a/src/blocks/CMakeLists.txt b/src/blocks/CMakeLists.txt index 4020132a9..66512e7ab 100644 --- a/src/blocks/CMakeLists.txt +++ b/src/blocks/CMakeLists.txt @@ -30,9 +30,9 @@ if(APPLE) add_library(blocks STATIC blockexports.c) set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C) else() - add_custom_command(OUTPUT blocks.o COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ld -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat) - add_library(blocks STATIC blocks.o blockexports.c) + add_custom_command(OUTPUT blocks.o COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ld -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat) + add_custom_command(OUTPUT testnet_blocks.o COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ld -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat) + add_library(blocks STATIC blocks.o testnet_blocks.o blockexports.c) set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C) endif() - diff --git a/src/blocks/blockexports.c b/src/blocks/blockexports.c index cea72b299..3fa3d4d07 100644 --- a/src/blocks/blockexports.c +++ b/src/blocks/blockexports.c @@ -9,16 +9,22 @@ extern const struct mach_header _mh_execute_header; extern const struct mach_header_64 _mh_execute_header; #endif -const unsigned char *get_blocks_dat_start() +const unsigned char *get_blocks_dat_start(int testnet) { size_t size; - return getsectiondata(&_mh_execute_header, "__DATA", "__blocks_dat", &size); + if (testnet) + return getsectiondata(&_mh_execute_header, "__DATA", "__testnet_blocks_dat", &size); + else + return getsectiondata(&_mh_execute_header, "__DATA", "__blocks_dat", &size); } -size_t get_blocks_dat_size() +size_t get_blocks_dat_size(int testnet) { size_t size; - getsectiondata(&_mh_execute_header, "__DATA", "__blocks_dat", &size); + if (testnet) + getsectiondata(&_mh_execute_header, "__DATA", "__testnet_blocks_dat", &size); + else + getsectiondata(&_mh_execute_header, "__DATA", "__blocks_dat", &size); return size; } @@ -27,22 +33,34 @@ size_t get_blocks_dat_size() #if defined(_WIN32) && !defined(_WIN64) #define _binary_blocks_start binary_blocks_dat_start #define _binary_blocks_end binary_blocks_dat_end +#define _binary_testnet_blocks_start binary_testnet_blocks_dat_start +#define _binary_testnet_blocks_end binary_testnet_blocks_dat_end #else #define _binary_blocks_start _binary_blocks_dat_start #define _binary_blocks_end _binary_blocks_dat_end +#define _binary_testnet_blocks_start _binary_testnet_blocks_dat_start +#define _binary_testnet_blocks_end _binary_testnet_blocks_dat_end #endif extern const unsigned char _binary_blocks_start[]; extern const unsigned char _binary_blocks_end[]; +extern const unsigned char _binary_testnet_blocks_start[]; +extern const unsigned char _binary_testnet_blocks_end[]; -const unsigned char *get_blocks_dat_start(void) +const unsigned char *get_blocks_dat_start(int testnet) { - return _binary_blocks_start; + if (testnet) + return _binary_testnet_blocks_start; + else + return _binary_blocks_start; } -size_t get_blocks_dat_size(void) +size_t get_blocks_dat_size(int testnet) { - return (size_t) (_binary_blocks_end - _binary_blocks_start); + if (testnet) + return (size_t) (_binary_testnet_blocks_end - _binary_testnet_blocks_start); + else + return (size_t) (_binary_blocks_end - _binary_blocks_start); } #endif diff --git a/src/blocks/blocks.h b/src/blocks/blocks.h index 76a08c89d..b842009a4 100644 --- a/src/blocks/blocks.h +++ b/src/blocks/blocks.h @@ -5,8 +5,8 @@ extern "C" { #endif -const unsigned char *get_blocks_dat_start(); -size_t get_blocks_dat_size(); +const unsigned char *get_blocks_dat_start(int testnet); +size_t get_blocks_dat_size(int testnet); #ifdef __cplusplus } diff --git a/src/blocks/testnet_blocks.dat b/src/blocks/testnet_blocks.dat new file mode 100644 index 000000000..e69de29bb diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index e06c3c08c..c38b58841 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -352,13 +352,14 @@ bool Blockchain::init(BlockchainDB* db, const bool testnet) m_async_pool.create_thread(boost::bind(&boost::asio::io_service::run, &m_async_service)); #if defined(PER_BLOCK_CHECKPOINT) - if (m_fast_sync && !testnet && get_blocks_dat_start() != nullptr) + if (m_fast_sync && get_blocks_dat_start(testnet) != nullptr) { - if (get_blocks_dat_size() > 4) + if (get_blocks_dat_size(testnet) > 4) { - const unsigned char *p = get_blocks_dat_start(); - uint32_t nblocks = *(uint32_t *) p; - if(nblocks > 0 && nblocks > m_db->height()) + const unsigned char *p = get_blocks_dat_start(testnet); + const uint32_t nblocks = *p | ((*(p+1))<<8) | ((*(p+2))<<16) | ((*(p+3))<<24); + const size_t size_needed = 4 + nblocks * sizeof(crypto::hash); + if(nblocks > 0 && nblocks > m_db->height() && get_blocks_dat_size(testnet) >= size_needed) { LOG_PRINT_L0("Loading precomputed blocks: " << nblocks); p += sizeof(uint32_t);