Last active
December 23, 2015 22:39
-
-
Save Bobalot/6704337 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 | |
bootstrap.dat is the raw blocks in the format | |
[magic bytes] [block length] [serialized block] | |
*/ | |
#include <future> | |
#include <bitcoin/bitcoin.hpp> | |
#define LOG_BOOTSTRAP "bootstrap" | |
using namespace bc; | |
using std::placeholders::_1; | |
using std::placeholders::_2; | |
bool stopped = false; | |
void signal_handler(int signal) | |
{ | |
log_info(LOG_BOOTSTRAP) << "Caught signal: " << signal; | |
stopped = true; | |
} | |
int main(int argc, char** argv) | |
{ | |
if (argc != 3) | |
{ | |
std::cout << "Usage: " | |
"bootstrap BLOCKCHAIN_DIR BOOTSTRAP_DATFILE" << std::endl; | |
return 1; | |
} | |
const std::string dbpath = argv[1]; | |
const std::string bootstrapfile = argv[2]; | |
// 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(); | |
const hash_digest genesis_hash = hash_block_header(first_block.header); | |
// Total number of blocks parsed from the bootstrap.dat | |
int blocks_parsed = 0; | |
// position of the pointer in the buffer | |
int buffer_position = 0; | |
std::ifstream fin(bootstrapfile, std::ios::in | std::ios::binary); | |
if (!fin) | |
{ | |
log_error(LOG_BOOTSTRAP) << "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]); | |
}; | |
// This will return false when there are not enough bytes left | |
// in the file to fill the buffer, meaning we may miss the last block | |
while(!stopped) | |
{ | |
fin.read((char*) &buffer[0], buffer_size); | |
buffer_position = 0; | |
while(buffer_position < buffer_size - 8 && !stopped) | |
{ | |
// Check we have the magic bytes. This will automatically exit | |
// if reach an incorrect part or the end of the file. | |
if (buffer[buffer_position] != 0xf9 || | |
buffer[buffer_position + 1] != 0xbe || | |
buffer[buffer_position + 2] != 0xb4 || | |
buffer[buffer_position + 3] != 0xd9) | |
{ | |
stopped = true; | |
break; | |
} | |
// 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; | |
auto begin_iter = buffer.begin() + buffer_position + 8; | |
auto end_iter = begin_iter + block_size; | |
block_type blk; | |
satoshi_load(begin_iter, end_iter, blk); | |
// Assert we match the genesis block hash | |
BITCOIN_ASSERT(blocks_parsed != 0 || | |
hash_block_header(blk.header) == genesis_hash); | |
std::promise<std::error_code> ecp; | |
auto block_imported = [&ecp](const std::error_code& ec) | |
{ | |
ecp.set_value(ec); | |
}; | |
chain.import(blk, blocks_parsed, block_imported); | |
std::error_code ec = ecp.get_future().get(); | |
hash_digest block_hash = hash_block_header(blk.header); | |
usleep(1000); | |
if (ec) | |
{ | |
stopped = true; | |
break; | |
} | |
else | |
{ | |
log_info(LOG_BOOTSTRAP) | |
<< "Block #" << blocks_parsed << " " << hash_block_header(blk.header); | |
} | |
buffer_position += block_size + 8; | |
blocks_parsed++; | |
} | |
log_info(LOG_BOOTSTRAP) | |
<< "Rewind"; | |
// 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