forked from ton-blockchain/ton
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtest-ton-collator.cpp
499 lines (451 loc) · 18.9 KB
/
test-ton-collator.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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
/*
This file is part of TON Blockchain source code.
TON Blockchain is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
TON Blockchain 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
You must obey the GNU General Public License in all respects for all
of the code used other than OpenSSL. If you modify file(s) with this
exception, you may extend this exception to your version of the file(s),
but you are not obligated to do so. If you do not wish to do so, delete this
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2017-2020 Telegram Systems LLP
*/
#include "adnl/adnl.h"
#include "adnl/utils.hpp"
#include "auto/tl/ton_api_json.h"
#include "dht/dht.h"
#include "overlay/overlays.h"
#include "td/utils/OptionParser.h"
#include "td/utils/Time.h"
#include "td/utils/filesystem.h"
#include "td/utils/format.h"
#include "td/utils/Random.h"
#include "td/utils/port/signals.h"
#include "td/utils/port/FileFd.h"
#include "catchain/catchain.h"
#include "validator-session/validator-session.h"
#include "validator/manager-disk.h"
#include "td/utils/filesystem.h"
#include "td/utils/port/path.h"
#include "ton/ton-types.h"
#include "ton/ton-tl.hpp"
#include "ton/ton-io.hpp"
#include "validator/fabric.h"
#include "validator/impl/collator.h"
#include "crypto/vm/vm.h"
#include "crypto/block/block-db.h"
#include "common/errorlog.h"
#if TD_DARWIN || TD_LINUX
#include <unistd.h>
#endif
#include <iostream>
#include <sstream>
int verbosity;
struct IntError {
std::string err_msg;
IntError(std::string _msg) : err_msg(_msg) {
}
IntError(const char *_msg) : err_msg(_msg) {
}
IntError(td::Status _err) : err_msg(_err.to_string()) {
}
void show() const {
std::cerr << "fatal: " << err_msg << std::endl;
}
};
class TestNode : public td::actor::Actor {
private:
td::actor::ActorOwn<ton::validator::ValidatorManagerInterface> validator_manager_;
std::string db_root_ = "/var/ton-work/db/";
std::string global_config_;
td::Ref<ton::validator::ValidatorManagerOptions> opts_;
ton::ZeroStateIdExt zero_id_;
td::BufferSlice bs_;
std::vector<td::BufferSlice> ext_msgs_;
std::vector<td::BufferSlice> top_shard_descrs_;
std::string zero_file_path_;
bool need_save_file_{false};
bool tdescr_save_{false};
std::string tdescr_pfx_;
ton::BlockIdExt shard_top_block_id_;
ton::ShardIdFull shard_{ton::masterchainId, ton::shardIdAll};
public:
void set_db_root(std::string db_root) {
db_root_ = db_root;
}
void set_global_config_path(std::string path) {
global_config_ = path;
}
void set_zero_root_hash(td::Bits256 hash) {
zero_id_.root_hash = hash;
}
void set_zero_file_hash(td::Bits256 hash) {
zero_id_.file_hash = hash;
}
void set_shard(ton::ShardIdFull shard) {
LOG(DEBUG) << "setting shard to " << shard.to_str();
shard_ = shard;
}
void set_shard_top_block(ton::BlockIdExt block_id) {
shard_top_block_id_ = block_id;
}
void set_top_descr_prefix(std::string tdescr_pfx) {
tdescr_pfx_ = tdescr_pfx;
tdescr_save_ = true;
}
void set_collator_flags(int flags) {
ton::collator_settings |= flags;
}
void start_up() override {
}
void alarm() override {
}
TestNode() {
zero_id_.root_hash.clear();
zero_id_.file_hash.clear();
}
void set_zero_file(std::string filename) {
try {
auto res1 = block::load_binary_file(filename);
if (res1.is_error()) {
throw IntError{res1.move_as_error()};
}
bs_ = res1.move_as_ok();
auto res = ton::validator::create_shard_state(
ton::BlockIdExt{ton::BlockId{shard_.workchain, ton::shardIdAll, 0}, zero_id_.root_hash, zero_id_.file_hash},
bs_.clone());
if (res.is_error()) {
throw IntError{res.move_as_error()};
}
auto state = res.move_as_ok();
ton::FileHash fhash = block::compute_file_hash(bs_.as_slice());
ton::RootHash rhash = state->root_hash();
CHECK(!fhash.is_zero());
CHECK(!rhash.is_zero());
if (!zero_id_.root_hash.is_zero()) {
if (zero_id_.root_hash != rhash) {
throw IntError{std::string{"root hash mismatch: expected "} + zero_id_.root_hash.to_hex() + " found " +
rhash.to_hex()};
}
}
zero_id_.root_hash = rhash;
if (!zero_id_.file_hash.is_zero()) {
if (zero_id_.file_hash != fhash) {
throw IntError{std::string{"file hash mismatch: expected "} + zero_id_.file_hash.to_hex() + " found " +
fhash.to_hex()};
}
}
zero_id_.file_hash = fhash;
need_save_file_ = true;
zero_file_path_ = filename;
} catch (IntError err) {
err.show();
std::exit(7);
}
}
void load_ext_message(std::string filename) {
try {
auto res1 = block::load_binary_file(filename);
if (res1.is_error()) {
throw IntError{res1.move_as_error()};
}
ext_msgs_.emplace_back(res1.move_as_ok());
} catch (IntError err) {
err.show();
std::exit(7);
}
}
void load_shard_block_message(std::string filename) {
try {
auto res1 = block::load_binary_file(filename);
if (res1.is_error()) {
throw IntError{res1.move_as_error()};
}
top_shard_descrs_.emplace_back(res1.move_as_ok());
} catch (IntError err) {
err.show();
std::exit(7);
}
}
void do_save_file() {
std::string fname = db_root_ + "/static/";
fname.reserve(fname.size() + 2 * 3 + 64);
static const char hex_digits[] = "0123456789ABCDEF";
for (int i = 0; i < 2 * 0; i++) {
unsigned x = zero_id_.file_hash.data()[i];
fname.push_back(hex_digits[(x >> 4) & 15]);
fname.push_back(hex_digits[x & 15]);
fname.push_back('/');
td::mkdir(fname).ensure();
}
for (int i = 0; i < 32; i++) {
unsigned x = zero_id_.file_hash.data()[i];
fname.push_back(hex_digits[(x >> 4) & 15]);
fname.push_back(hex_digits[x & 15]);
}
auto res1 = block::load_binary_file(fname);
if (res1.is_ok()) {
if (res1.move_as_ok() != bs_) {
std::cerr << "fatal: " << fname << " has wrong content" << std::endl;
std::exit(7);
}
} else {
auto res = block::save_binary_file(fname, bs_.clone());
if (res.is_error()) {
std::cerr << "fatal: cannot write file " << fname << ": " << res.to_string();
std::exit(7);
}
}
}
td::Status create_validator_options() {
if(!global_config_.length()) {
LOG(INFO) << "no global config file passed. Using zero-init config";
opts_ = ton::validator::ValidatorManagerOptions::create(
ton::BlockIdExt{ton::masterchainId, ton::shardIdAll, 0, ton::RootHash::zero(), ton::FileHash::zero()},
ton::BlockIdExt{ton::masterchainId, ton::shardIdAll, 0, ton::RootHash::zero(), ton::FileHash::zero()});
return td::Status::OK();
}
TRY_RESULT_PREFIX(conf_data, td::read_file(global_config_), "failed to read: ");
TRY_RESULT_PREFIX(conf_json, td::json_decode(conf_data.as_slice()), "failed to parse json: ");
ton::ton_api::config_global conf;
TRY_STATUS_PREFIX(ton::ton_api::from_json(conf, conf_json.get_object()), "json does not fit TL scheme: ");
auto zero_state = ton::create_block_id(conf.validator_->zero_state_);
ton::BlockIdExt init_block;
if (!conf.validator_->init_block_) {
LOG(INFO) << "no init block in config. using zero state";
init_block = zero_state;
} else {
init_block = ton::create_block_id(conf.validator_->init_block_);
}
opts_ = ton::validator::ValidatorManagerOptions::create(zero_state, init_block);
std::vector<ton::BlockIdExt> h;
for (auto &x : conf.validator_->hardforks_) {
auto b = ton::create_block_id(x);
if (!b.is_masterchain()) {
return td::Status::Error(ton::ErrorCode::error,
"[validator/hardforks] section contains not masterchain block id");
}
if (!b.is_valid_full()) {
return td::Status::Error(ton::ErrorCode::error, "[validator/hardforks] section contains invalid block_id");
}
for (auto &y : h) {
if (y.is_valid() && y.seqno() >= b.seqno()) {
y.invalidate();
}
}
h.push_back(b);
}
opts_.write().set_hardforks(std::move(h));
LOG(INFO) << "Hardforks num in config: "<< opts_->get_hardforks().size();
return td::Status::OK();
}
void run() {
zero_id_.workchain = ton::masterchainId;
td::mkdir(db_root_).ensure();
ton::errorlog::ErrorLog::create(db_root_);
if (!shard_.is_masterchain() && need_save_file_) {
td::mkdir(db_root_ + "/static").ensure();
do_save_file();
}
auto Sr = create_validator_options();
if (Sr.is_error()) {
LOG(ERROR) << "failed to load global config'" << global_config_ << "': " << Sr;
std::_Exit(2);
}
auto opts = opts_;
opts.write().set_initial_sync_disabled(true);
validator_manager_ = ton::validator::ValidatorManagerDiskFactory::create(ton::PublicKeyHash::zero(), opts, shard_,
shard_top_block_id_, db_root_);
for (auto &msg : ext_msgs_) {
td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManager::new_external_message,
std::move(msg), 0);
}
for (auto &topmsg : top_shard_descrs_) {
td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManager::new_shard_block, ton::BlockIdExt{},
0, std::move(topmsg));
}
class Callback : public ton::validator::ValidatorManagerInterface::Callback {
private:
td::actor::ActorId<ton::validator::ValidatorManagerInterface> id_;
bool tdescr_save_;
std::string tdescr_pfx_;
int tdescr_cnt_ = 0;
public:
Callback(td::actor::ActorId<ton::validator::ValidatorManagerInterface> id, bool tdescr_save = false,
std::string tdescr_pfx = "")
: id_(id), tdescr_save_(tdescr_save), tdescr_pfx_(tdescr_pfx) {
}
void initial_read_complete(ton::validator::BlockHandle handle) override {
td::actor::send_closure(id_, &ton::validator::ValidatorManager::sync_complete,
td::PromiseCreator::lambda([](td::Unit) {}));
}
void add_shard(ton::ShardIdFull) override {
}
void del_shard(ton::ShardIdFull) override {
}
void send_ihr_message(ton::AccountIdPrefixFull dst, td::BufferSlice data) override {
}
void send_ext_message(ton::AccountIdPrefixFull dst, td::BufferSlice data) override {
}
void send_shard_block_info(ton::BlockIdExt block_id, ton::CatchainSeqno cc_seqno, td::BufferSlice data) override {
++tdescr_cnt_;
if (!tdescr_save_) {
LOG(INFO) << "Ignoring newly-generated ShardTopBlockDescr for " << block_id.to_str();
} else {
char buffer[16];
sprintf(buffer, "%d.boc", tdescr_cnt_);
std::string fname = std::string{tdescr_pfx_.empty() ? "tdescr" : tdescr_pfx_} + buffer;
LOG(INFO) << "Saving newly-generated ShardTopBlockDescr for " << block_id.to_str() << " into file " << fname;
auto res = block::save_binary_file(fname, std::move(data));
if (res.is_error()) {
LOG(ERROR) << "Cannot save ShardTopBlockDescr for " << block_id.to_str() << " into file " << fname << " : "
<< res.move_as_error().to_string();
}
}
}
void send_block_candidate(ton::BlockIdExt block_id, ton::CatchainSeqno cc_seqno, td::uint32 validator_set_hash,
td::BufferSlice data) override {
}
void send_broadcast(ton::BlockBroadcast broadcast, bool custom_overlays_only) override {
}
void download_block(ton::BlockIdExt block_id, td::uint32 priority, td::Timestamp timeout,
td::Promise<ton::ReceivedBlock> promise) override {
}
void download_zero_state(ton::BlockIdExt block_id, td::uint32 priority, td::Timestamp timeout,
td::Promise<td::BufferSlice> promise) override {
}
void download_persistent_state(ton::BlockIdExt block_id, ton::BlockIdExt masterchain_block_id,
td::uint32 priority, td::Timestamp timeout,
td::Promise<td::BufferSlice> promise) override {
}
void download_block_proof(ton::BlockIdExt block_id, td::uint32 priority, td::Timestamp timeout,
td::Promise<td::BufferSlice> promise) override {
}
void download_block_proof_link(ton::BlockIdExt block_id, td::uint32 priority, td::Timestamp timeout,
td::Promise<td::BufferSlice> promise) override {
}
void get_next_key_blocks(ton::BlockIdExt block_id, td::Timestamp timeout,
td::Promise<std::vector<ton::BlockIdExt>> promise) override {
}
void download_archive(ton::BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout,
td::Promise<std::string> promise) override {
}
void new_key_block(ton::validator::BlockHandle handle) override {
}
};
td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::install_callback,
std::make_unique<Callback>(validator_manager_.get(), tdescr_save_, tdescr_pfx_),
td::PromiseCreator::lambda([](td::Unit) {}));
}
};
td::Result<td::Bits256> get_uint256(td::Slice str) {
TRY_RESULT(R, td::base64url_decode(str));
if (R.length() != 32) {
return td::Status::Error("uint256 must have 64 bytes");
}
td::Bits256 x;
as_slice(x).copy_from(td::Slice(R));
return x;
}
int parse_hex_digit(int c) {
if (c >= '0' && c <= '9') {
return c - '0';
}
c |= 0x20;
if (c >= 'a' && c <= 'f') {
return c - 'a' + 10;
}
return -1;
}
int main(int argc, char *argv[]) {
SET_VERBOSITY_LEVEL(verbosity_INFO);
td::set_default_failure_signal_handler().ensure();
vm::init_vm().ensure();
td::actor::ActorOwn<TestNode> x;
td::OptionParser p;
p.set_description("test collate block");
p.add_option('h', "help", "prints_help", [&]() {
char b[10240];
td::StringBuilder sb(td::MutableSlice{b, 10000});
sb << p;
std::cout << sb.as_cslice().c_str();
std::exit(2);
});
p.add_option('Z', "zero-root-hash", "zero state root hash (base64url-encoded)", [&](td::Slice fname) {
td::actor::send_closure(x, &TestNode::set_zero_root_hash, get_uint256(fname).move_as_ok());
});
p.add_option('F', "zero-file-hash", "zero state file hash (base64url-encoded)", [&](td::Slice fname) {
td::actor::send_closure(x, &TestNode::set_zero_file_hash, get_uint256(fname).move_as_ok());
});
p.add_option('z', "zero-state-file", "zero state file",
[&](td::Slice fname) { td::actor::send_closure(x, &TestNode::set_zero_file, fname.str()); });
p.add_option('D', "db", "root for dbs",
[&](td::Slice fname) { td::actor::send_closure(x, &TestNode::set_db_root, fname.str()); });
p.add_option('C', "config", "global config path",
[&](td::Slice fname) { td::actor::send_closure(x, &TestNode::set_global_config_path, fname.str()); });
p.add_option('m', "ext-message", "binary file with serialized inbound external message",
[&](td::Slice fname) { td::actor::send_closure(x, &TestNode::load_ext_message, fname.str()); });
p.add_option('M', "top-shard-message", "binary file with serialized shard top block description",
[&](td::Slice fname) { td::actor::send_closure(x, &TestNode::load_shard_block_message, fname.str()); });
p.add_option('v', "verbosity", "set verbosity level", [&](td::Slice arg) {
int v = VERBOSITY_NAME(FATAL) + (verbosity = td::to_integer<int>(arg));
SET_VERBOSITY_LEVEL(v);
});
p.add_checked_option('w', "workchain", "<workchain>[:<shard>]\tcollate block in this workchain", [&](td::Slice arg) {
ton::ShardId shard = 0;
auto pos = std::min(arg.find(':'), arg.size());
TRY_RESULT(workchain, td::to_integer_safe<int>(arg.substr(0, pos)));
int s = 60;
while (++pos < arg.size()) {
int x = parse_hex_digit(arg[pos]);
if (x < 0 || s < 0) {
return td::Status::Error("cannot parse hexadecimal shard id (prefix)");
}
shard |= (ton::ShardId(x) << s);
s -= 4;
}
td::actor::send_closure(x, &TestNode::set_shard, ton::ShardIdFull{workchain, shard ? shard : ton::shardIdAll});
return td::Status::OK();
});
p.add_option('S', "want-split", "forces setting want_split in the header of new shard block",
[&]() { td::actor::send_closure(x, &TestNode::set_collator_flags, 1); });
p.add_option('G', "want-merge", "forces setting want_merge in the header of new shard block",
[&]() { td::actor::send_closure(x, &TestNode::set_collator_flags, 2); });
p.add_option('s', "save-top-descr", "saves generated shard top block description into files with specified prefix",
[&](td::Slice arg) { td::actor::send_closure(x, &TestNode::set_top_descr_prefix, arg.str()); });
p.add_checked_option('T', "top-block", "BlockIdExt of top block (new block will be generated atop of it)",
[&](td::Slice arg) {
ton::BlockIdExt block_id;
if (block::parse_block_id_ext(arg, block_id)) {
LOG(INFO) << "setting previous block to " << block_id.to_str();
td::actor::send_closure(x, &TestNode::set_shard_top_block, block_id);
return td::Status::OK();
} else {
return td::Status::Error("cannot parse BlockIdExt");
}
});
p.add_option('d', "daemonize", "set SIGHUP", [&]() {
td::set_signal_handler(td::SignalType::HangUp, [](int sig) {
#if TD_DARWIN || TD_LINUX
close(0);
setsid();
#endif
}).ensure();
});
td::actor::Scheduler scheduler({7});
scheduler.run_in_context([&] { x = td::actor::create_actor<TestNode>("testnode"); });
scheduler.run_in_context([&] { p.run(argc, argv).ensure(); });
scheduler.run_in_context([&] { td::actor::send_closure(x, &TestNode::run); });
scheduler.run();
return 0;
}