forked from RevCBH/libbitcoin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
bootstrap-fast.cpp
192 lines (162 loc) · 6.5 KB
/
bootstrap-fast.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/*
* Copyright (c) 2011-2013 libbitcoin developers (see AUTHORS)
*
* This file is part of libbitcoin.
*
* libbitcoin is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License with
* additional permissions to the one published by the Free Software
* Foundation, either version 3 of the License, or (at your option)
* any later version. For more information see LICENSE.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
Imports blocks from bootstrap.dat
bootstrap.dat is the raw blocks in the format
[magic bytes] [block length] [serialized block]
No validation is performed on the blocks, ensure the data is correct.
*/
#include <future>
#include <bitcoin/bitcoin.hpp>
#include <time.h>
#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-fast BLOCKCHAIN_DIR BOOTSTRAP_DATFILE" << std::endl <<
"Ensure the BLOCKCHAIN_DIR is empty and the " << std::endl <<
"BOOTSTRAP_DATFILE is correct, no validation is done" << 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 = 10 * 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.
// Serialized format has its bytes reversed.
uint32_t bootstrap_magic_value = buffer[buffer_position] +
(buffer[buffer_position + 1] << 8) +
(buffer[buffer_position + 2] << 16) +
(buffer[buffer_position + 3] << 24);
if (bootstrap_magic_value != magic_value())
{
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::error_code ec;
block_info info;
bool finished = false;
auto block_imported = [&ec, &blocks_parsed, &finished](
const std::error_code& cec)
{
log_debug(LOG_BOOTSTRAP) << blocks_parsed << " Block imported.";
// info = blk_info;
ec = cec;
finished = true;
};
chain.import(blk, blocks_parsed ,block_imported);
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 10;
while (!finished)
nanosleep(&ts, NULL);
hash_digest block_hash = hash_block_header(blk.header);
buffer_position += block_size + 8;
blocks_parsed++;
}
log_info(LOG_BOOTSTRAP)
<< "Rewinding file buffer";
// 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;
}