mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	* Bugfixes in TVM and node * Upgrade to C++17 * Improve GitHub builds * Fix existing tests and partially integrate them into builds --------- Co-authored-by: neodiX42 <namlem@gmail.com> Co-authored-by: EmelyanenkoK <emelyanenko.kirill@gmail.com>
		
			
				
	
	
		
			496 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			496 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* 
 | |
|     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));
 | |
|     }
 | |
|     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_broadcast(ton::BlockBroadcast broadcast) 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;
 | |
| }
 |