Skip to content

Instantly share code, notes, and snippets.

@Bobalot
Last active December 22, 2015 13:08
Show Gist options
  • Save Bobalot/93617a8f2d5ac8a5cf12 to your computer and use it in GitHub Desktop.
Save Bobalot/93617a8f2d5ac8a5cf12 to your computer and use it in GitHub Desktop.
/*
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