-
-
Save Bobalot/93617a8f2d5ac8a5cf12 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Imports blocks from bootstrap.dat | |
*/ | |
#include <future> | |
#include <bitcoin/bitcoin.hpp> | |
using namespace bc; | |
using std::placeholders::_1; | |
using std::placeholders::_2; | |
void handle_store(const std::error_code& ec, block_info info, | |
const hash_digest& block_hash) | |
{ | |
// We need orphan blocks so we can do the next getblocks round | |
if (ec && info.status != block_status::orphan) | |
{ | |
log_error("bootstrap") | |
<< "Storing block " << encode_hex(block_hash) | |
<< ": " << ec.message(); | |
return; | |
} | |
switch (info.status) | |
{ | |
case block_status::orphan: | |
// TODO: Make more efficient by storing block hash | |
// and next time do not download orphan block again. | |
// Remember to remove from list once block is no longer orphan | |
log_error("bootstrap") | |
<< "Orphan block " << encode_hex(block_hash); | |
// fetch_block_locator(chain_, | |
// strand_.wrap(std::bind(&poller::ask_blocks, | |
// this, _1, _2, block_hash, node))); | |
break; | |
case block_status::rejected: | |
log_error("bootstrap") | |
<< "Rejected block " << encode_hex(block_hash); | |
break; | |
case block_status::confirmed: | |
log_info("bootstrap") | |
<< "Block #" << info.height << " " << encode_hex(block_hash); | |
break; | |
} | |
} | |
int main(int argc, char** argv) | |
{ | |
if (argc != 2) | |
return 1; | |
const std::string dbpath = argv[1]; | |
// Threadpool context containing 1 thread. | |
threadpool pool(1); | |
// leveldb_blockchain operations execute in pool's thread. | |
leveldb_blockchain chain(pool); | |
// Completion handler for starting the leveldb_blockchain. | |
// Does nothing. | |
auto blockchain_start = [](const std::error_code& ec) {}; | |
// Start blockchain with a database path. | |
chain.start(dbpath, blockchain_start); | |
// First block is the genesis block. | |
block_type first_block = genesis_block(); | |
int last_magic_bytes = 0; | |
int blocks_parsed = 0; | |
int buffer_position = 0; | |
int file_position = 0; | |
const std::string bootstrapfile = "bootstrap.dat"; | |
std::ifstream fin(bootstrapfile, std::ios::in | std::ios::binary); | |
if (!fin) | |
{ | |
log_error() << "Could not open bootstrap.dat file"; | |
fin.close(); | |
pool.stop(); | |
pool.join(); | |
chain.stop(); | |
return -1; | |
} | |
constexpr size_t max_block_size = 1000000; | |
// Allocate a 2 * max_block_size buffer, so we are sure to read at least | |
// one full block into the buffer, or many smaller ones. | |
constexpr size_t buffer_size = 2 * max_block_size; | |
int block_size; | |
data_chunk buffer(buffer_size); | |
// The total size of the block is endian reversed | |
// This lambda function will read the chunk from its | |
// first byte at position pos in the buffer and return | |
// the integer representation of the block size in bytes. | |
auto get_block_size = | |
[](const data_chunk& buffer, const int pos) | |
{ | |
return (int) (buffer[pos + 3] << 24) + | |
(int) (buffer[pos + 2] << 16) + | |
(int) (buffer[pos + 1] << 8) + | |
(int) (buffer[pos]); | |
}; | |
while(fin.read((char*) &buffer[0], buffer_size)) | |
{ | |
buffer_position = 0; | |
while(buffer_position < buffer_size - 8) | |
{ | |
// Assert we have the magic bytes. | |
BITCOIN_ASSERT( buffer[buffer_position] == 0xf9 && | |
buffer[buffer_position + 1] == 0xbe && | |
buffer[buffer_position + 2] == 0xb4 && | |
buffer[buffer_position + 3] == 0xd9 ); | |
// Get the block size from the reversed endain field. | |
block_size = get_block_size(buffer, buffer_position + 4); | |
// If the rest of the block isn't inside the buffer break out | |
if (buffer_position + block_size + 8 > buffer_size) | |
break; | |
std::promise<std::error_code> ec_promise; | |
block_type blk; | |
satoshi_load(buffer.begin() + buffer_position + 8, | |
buffer.begin() + buffer_position + 8 + block_size, | |
blk); | |
auto block_imported = | |
[&ec_promise](const std::error_code& ec, const block_info info, | |
const hash_digest& block_hash) | |
{ | |
handle_store(ec, info, block_hash); | |
ec_promise.set_value(ec); | |
}; | |
chain.store(blk, | |
std::bind(block_imported, | |
_1, _2, hash_block_header(blk.header))); | |
std::error_code ec = ec_promise.get_future().get(); | |
buffer_position += block_size + 8; | |
blocks_parsed++; | |
} | |
// Rewind to the last good magic bytes, which didn't have enough space | |
// left in the buffer for the block | |
fin.seekg(buffer_position - buffer_size, std::ios::cur); | |
} | |
fin.close(); | |
// All threadpools stopping in parallel... | |
pool.stop(); | |
// ... Make them all join main thread and wait until they finish. | |
pool.join(); | |
// Now safely close leveldb_blockchain. | |
chain.stop(); | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment