1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

update tonlib

tonlib: update
collator: increased collation speed for masterchain
fift: bugfixes
This commit is contained in:
ton 2019-10-04 16:08:02 +04:00
parent 7ea00ebfcf
commit dd745485e2
27 changed files with 313 additions and 172 deletions

View file

@ -434,9 +434,10 @@ TEST(Tonlib, KeysApi) {
using tonlib_api::make_object;
Client client;
td::mkdir("testdir").ignore();
// init
sync_send(client, make_object<tonlib_api::init>(
make_object<tonlib_api::options>(nullptr, make_object<tonlib_api::keyStoreTypeDirectory>("."))))
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(
nullptr, make_object<tonlib_api::keyStoreTypeDirectory>("testdir"))))
.ensure();
auto local_password = td::SecureString("local password");
auto mnemonic_password = td::SecureString("mnemonic password");

View file

@ -61,15 +61,14 @@ class ExtClient {
auto raw_query = ton::serialize_tl_object(&query, true);
td::uint32 tag = td::Random::fast_uint32();
VLOG(lite_server) << "send query to liteserver: " << tag << " " << to_string(query);
td::BufferSlice liteserver_query =
ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_query>(std::move(raw_query)), true);
if (seq_no >= 0) {
auto wait = ton::lite_api::liteServer_waitMasterchainSeqno(seq_no, 5000);
VLOG(lite_server) << " with prefix " << to_string(wait);
auto prefix = ton::serialize_tl_object(&wait, true);
liteserver_query = td::BufferSlice(PSLICE() << prefix.as_slice() << liteserver_query.as_slice());
raw_query = td::BufferSlice(PSLICE() << prefix.as_slice() << raw_query.as_slice());
}
td::BufferSlice liteserver_query =
ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_query>(std::move(raw_query)), true);
send_raw_query(
std::move(liteserver_query), [promise = std::move(promise), tag](td::Result<td::BufferSlice> R) mutable {

View file

@ -69,7 +69,7 @@ td::Result<DecryptedKey> KeyStorage::export_decrypted_key(InputKey input_key) {
if (r_encrypted_data.is_error()) {
r_encrypted_data = kv_->get(to_file_name_old(input_key.key));
if (r_encrypted_data.is_ok()) {
LOG(WARNING) << "Restore private from deprecated location " << to_file_name_old(input_key.key) << " --> "
LOG(WARNING) << "Restore private key from deprecated location " << to_file_name_old(input_key.key) << " --> "
<< to_file_name(input_key.key);
TRY_STATUS_PREFIX(kv_->set(to_file_name(input_key.key), r_encrypted_data.ok()), TonlibError::Internal());
kv_->erase(to_file_name_old(input_key.key)).ignore();
@ -78,6 +78,21 @@ td::Result<DecryptedKey> KeyStorage::export_decrypted_key(InputKey input_key) {
TRY_RESULT_PREFIX(encrypted_data, std::move(r_encrypted_data), TonlibError::KeyUnknown());
EncryptedKey encrypted_key{std::move(encrypted_data), td::Ed25519::PublicKey(std::move(input_key.key.public_key)),
std::move(input_key.key.secret)};
{
auto r_decrypted_key = encrypted_key.decrypt(input_key.local_password.copy(), true, true);
if (r_decrypted_key.is_ok()) {
LOG(WARNING) << "Restore private from deprecated encryption " << to_file_name(input_key.key);
auto decrypted_key = r_decrypted_key.move_as_ok();
auto key = Key{encrypted_key.public_key.as_octet_string(), encrypted_key.secret.copy()};
auto new_encrypted_key = decrypted_key.encrypt(input_key.local_password.copy(), encrypted_key.secret);
CHECK(new_encrypted_key.public_key.as_octet_string() == encrypted_key.public_key.as_octet_string());
CHECK(new_encrypted_key.secret == encrypted_key.secret);
CHECK(new_encrypted_key.decrypt(input_key.local_password.copy()).ok().private_key.as_octet_string() ==
decrypted_key.private_key.as_octet_string());
kv_->set(to_file_name(key), new_encrypted_key.encrypted_data);
return std::move(decrypted_key);
}
}
TRY_RESULT_PREFIX(decrypted_key, encrypted_key.decrypt(std::move(input_key.local_password)),
TonlibError::KeyDecrypt());
return std::move(decrypted_key);
@ -139,14 +154,9 @@ td::Result<KeyStorage::ExportedPemKey> KeyStorage::export_pem_key(InputKey input
}
td::Result<KeyStorage::Key> KeyStorage::change_local_password(InputKey input_key, td::Slice new_local_password) {
auto new_secret =
DecryptedKey::change_local_password(input_key.key.secret, input_key.local_password, new_local_password);
Key res;
res.public_key = std::move(input_key.key.public_key);
res.secret = std::move(new_secret);
TRY_RESULT_PREFIX(value, kv_->get(to_file_name(input_key.key)), TonlibError::KeyUnknown());
TRY_STATUS_PREFIX(kv_->add(to_file_name(res), value), TonlibError::Internal());
return std::move(res);
auto old_name = to_file_name(input_key.key);
TRY_RESULT(decrypted_key, export_decrypted_key(std::move(input_key)));
return save_key(std::move(decrypted_key), new_local_password);
}
td::Result<KeyStorage::Key> KeyStorage::import_pem_key(td::Slice local_password, td::Slice key_password,

View file

@ -39,7 +39,7 @@ class KeyValueDir : public KeyValue {
}
td::Status erase(td::Slice key) override {
return td::unlink(key.str());
return td::unlink(to_file_path(key.str()));
}
void foreach_key(std::function<void(td::Slice)> f) override {

View file

@ -32,7 +32,8 @@ namespace tonlib {
//
td::StringBuilder& operator<<(td::StringBuilder& sb, const LastBlockState& state) {
return sb << td::tag("last_block", state.last_block_id.to_str())
<< td::tag("last_key_block", state.last_key_block_id.to_str()) << td::tag("utime", state.utime);
<< td::tag("last_key_block", state.last_key_block_id.to_str()) << td::tag("utime", state.utime)
<< td::tag("init_block", state.init_block_id.to_str());
}
LastBlock::LastBlock(ExtClientRef client, LastBlockState state, Config config, td::unique_ptr<Callback> callback)
@ -40,8 +41,7 @@ LastBlock::LastBlock(ExtClientRef client, LastBlockState state, Config config, t
client_.set_client(client);
state_.last_block_id = state_.last_key_block_id;
VLOG(last_block) << "check_init_block: skip - FIXME before release";
check_init_block_state_ = QueryState::Done;
VLOG(last_block) << "State: " << state_;
}
void LastBlock::get_last_block(td::Promise<LastBlockState> promise) {
@ -87,7 +87,7 @@ void LastBlock::sync_loop() {
} else {
check_init_block_state_ = QueryState::Active;
check_init_block_stats_.start();
if (state_.last_block_id.id.seqno >= config_.init_block_id.id.seqno) {
if (state_.last_key_block_id.id.seqno >= config_.init_block_id.id.seqno) {
VLOG(last_block) << "check_init_block: start - init_block -> last_block";
do_check_init_block(config_.init_block_id, state_.last_key_block_id);
} else {
@ -162,10 +162,18 @@ void LastBlock::update_state(block::BlockProofChain& chain) {
update_utime(chain.last_utime);
}
if (is_changed) {
callback_->on_state_changed(state_);
save_state();
}
}
void LastBlock::save_state() {
if (check_init_block_state_ != QueryState::Done) {
VLOG(last_block) << "skip `save_state` because `check_init_block` is not finished";
return;
}
callback_->on_state_changed(state_);
}
void LastBlock::on_block_proof(
ton::BlockIdExt from,
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof) {
@ -209,6 +217,9 @@ void LastBlock::on_init_block_proof(
if (chain->complete) {
VLOG(last_block) << "check_init_block: done\n" << check_init_block_stats_;
check_init_block_state_ = QueryState::Done;
if (update_init_block(config_.init_block_id)) {
save_state();
}
sync_loop();
} else {
do_check_init_block(chain->to, to);
@ -291,6 +302,22 @@ bool LastBlock::update_mc_last_key_block(ton::BlockIdExt mc_key_block_id) {
return false;
}
bool LastBlock::update_init_block(ton::BlockIdExt init_block_id) {
if (has_fatal_error()) {
return false;
}
if (!init_block_id.is_valid()) {
LOG(ERROR) << "Ignore invalid init block";
return false;
}
if (state_.init_block_id != init_block_id) {
state_.init_block_id = init_block_id;
LOG(INFO) << "Update init block id: " << state_.init_block_id.to_str();
return true;
}
return false;
}
void LastBlock::update_utime(td::int64 utime) {
if (state_.utime < utime) {
state_.utime = utime;

View file

@ -201,7 +201,9 @@ class LastBlock : public td::actor::Actor {
bool update_mc_last_block(ton::BlockIdExt mc_block_id);
bool update_mc_last_key_block(ton::BlockIdExt mc_key_block_id);
void update_utime(td::int64 utime);
bool update_init_block(ton::BlockIdExt init_block_id);
void save_state();
void on_sync_ok();
void on_sync_error(td::Status status);
void on_fatal_error(td::Status status);

View file

@ -108,7 +108,6 @@ class GetTransactionHistory : public td::actor::Actor {
void check(td::Status status) {
if (status.is_error()) {
LOG(ERROR) << status;
promise_.set_error(std::move(status));
stop();
}
@ -208,7 +207,7 @@ class GetRawAccountState : public td::actor::Actor {
auto cell = res.info.root;
std::ostringstream outp;
block::gen::t_Account.print_ref(outp, cell);
LOG(ERROR) << outp.str();
LOG(INFO) << outp.str();
if (cell.is_null()) {
return res;
}
@ -682,8 +681,9 @@ td::Result<tonlib_api::object_ptr<tonlib_api::raw_accountState>> to_raw_accountS
.as_slice()
.str();
}
return tonlib_api::make_object<tonlib_api::raw_accountState>(
raw_state.balance, std::move(code), std::move(data), to_transaction_id(raw_state.info), raw_state.info.gen_utime);
return tonlib_api::make_object<tonlib_api::raw_accountState>(raw_state.balance, std::move(code), std::move(data),
to_transaction_id(raw_state.info), raw_state.frozen_hash,
raw_state.info.gen_utime);
}
td::Result<std::string> to_std_address_or_throw(td::Ref<vm::CellSlice> cs) {
@ -803,7 +803,7 @@ td::Result<tonlib_api::object_ptr<tonlib_api::raw_transaction>> to_raw_transacti
std::ostringstream outp;
block::gen::t_Transaction.print_ref(outp, info.transaction);
LOG(ERROR) << outp.str();
LOG(INFO) << outp.str();
auto is_just = trans.r1.in_msg->prefetch_long(1);
if (is_just == trans.r1.in_msg->fetch_long_eof) {
@ -917,7 +917,7 @@ td::Result<tonlib_api::object_ptr<tonlib_api::generic_AccountState>> to_generic_
if (raw_state.code.is_null()) {
return tonlib_api::make_object<tonlib_api::generic_accountStateUninited>(
tonlib_api::make_object<tonlib_api::uninited_accountState>(raw_state.balance, to_transaction_id(raw_state.info),
raw_state.info.gen_utime));
raw_state.frozen_hash, raw_state.info.gen_utime));
}
auto code_hash = raw_state.code->prefetch_ref()->get_hash();
@ -953,7 +953,6 @@ td::Status TonlibClient::do_request(const tonlib_api::raw_sendMessage& request,
client_.send_query(ton::lite_api::liteServer_sendMessage(vm::std_boc_serialize(message).move_as_ok()),
[promise = std::move(promise)](auto r_info) mutable {
TRY_RESULT_PROMISE(promise, info, std::move(r_info));
LOG(ERROR) << "info: " << to_string(info);
promise.set_value(tonlib_api::make_object<tonlib_api::ok>());
});
return td::Status::OK();
@ -1042,7 +1041,6 @@ td::Status TonlibClient::do_request(const tonlib_api::testWallet_sendGrams& requ
return TonlibError::MessageTooLong();
}
TRY_RESULT(account_address, get_account_address(request.destination_->account_address_));
account_address.bounceable = false;
TRY_RESULT(input_key, from_tonlib(*request.private_key_));
auto address = GenericAccount::get_address(
0 /*zerochain*/, TestWallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy())));
@ -1116,7 +1114,6 @@ td::Status TonlibClient::do_request(const tonlib_api::wallet_sendGrams& request,
TRY_RESULT_PREFIX(valid_until, td::narrow_cast_safe<td::uint32>(request.valid_until_),
TonlibError::InvalidField("valid_until", "overflow"));
TRY_RESULT(account_address, get_account_address(request.destination_->account_address_));
account_address.bounceable = false;
TRY_RESULT(input_key, from_tonlib(*request.private_key_));
auto address = GenericAccount::get_address(
0 /*zerochain*/, Wallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy())));
@ -1168,7 +1165,6 @@ td::Status TonlibClient::do_request(const tonlib_api::testGiver_sendGrams& reque
return TonlibError::MessageTooLong();
}
TRY_RESULT(account_address, get_account_address(request.destination_->account_address_));
account_address.bounceable = false;
auto message = TestGiver::make_a_gift_message(request.seqno_, request.amount_, request.message_, account_address);
auto message_hash = message->get_hash().as_slice().str();
td::Promise<object_ptr<tonlib_api::ok>> new_promise =
@ -1245,7 +1241,7 @@ class GenericSendGrams : public TonlibQueryActor {
block::StdAddress source_address_;
tonlib_api::object_ptr<tonlib_api::generic_AccountState> destination_state_;
bool is_destination_bounce_{false};
bool is_destination_bounceable_{false};
void check(td::Status status) {
if (status.is_error()) {
@ -1263,7 +1259,7 @@ class GenericSendGrams : public TonlibQueryActor {
return TonlibError::EmptyField("destination");
}
TRY_RESULT(destination_address, get_account_address(send_grams_.destination_->account_address_));
is_destination_bounce_ = destination_address.bounceable;
is_destination_bounceable_ = destination_address.bounceable;
if (!send_grams_.source_) {
return TonlibError::EmptyField("destination");
@ -1333,9 +1329,23 @@ class GenericSendGrams : public TonlibQueryActor {
td::Status do_on_destination_state(td::Result<tonlib_api::object_ptr<tonlib_api::generic_AccountState>> r_state) {
TRY_RESULT(state, std::move(r_state));
destination_state_ = std::move(state);
if (destination_state_->get_id() == tonlib_api::generic_accountStateUninited::ID && is_destination_bounce_ &&
!send_grams_.allow_send_to_uninited_) {
return TonlibError::DangerousTransaction("Transfer to uninited wallet");
if (destination_state_->get_id() == tonlib_api::generic_accountStateUninited::ID && is_destination_bounceable_) {
//FIXME: after restoration of frozen accounts will be supported
if (!static_cast<tonlib_api::generic_accountStateUninited&>(*destination_state_)
.account_state_->frozen_hash_.empty()) {
return TonlibError::TransferToFrozen();
//return TonlibError::DangerousTransaction("Transfer to frozen wallet");
}
if (send_grams_.allow_send_to_uninited_) {
TRY_RESULT(destination_address, get_account_address(send_grams_.destination_->account_address_));
destination_address.bounceable = false;
auto new_destination_address = destination_address.rserialize(true);
LOG(INFO) << "Change destination address from bounceable to non-bounceable "
<< send_grams_.destination_->account_address_ << " -> " << new_destination_address;
send_grams_.destination_->account_address_ = std::move(new_destination_address);
} else {
return TonlibError::DangerousTransaction("Transfer to uninited wallet");
}
}
return do_loop();
}

View file

@ -128,6 +128,9 @@ struct TonlibError {
static td::Status NotEnoughFunds() {
return td::Status::Error(500, "NOT_ENOUGH_FUNDS");
}
static td::Status TransferToFrozen() {
return td::Status::Error(500, "TRANSFER_TO_FROZEN");
}
static td::Status LiteServer(td::int32 code, td::Slice message) {
auto f = [&](td::Slice code_description) { return LiteServer(code, code_description, message); };

View file

@ -35,36 +35,15 @@ DecryptedKey::DecryptedKey(RawDecryptedKey key)
: DecryptedKey(std::move(key.mnemonic_words), td::Ed25519::PrivateKey(key.private_key.copy())) {
}
td::SecureString DecryptedKey::change_local_password(td::Slice secret_str, td::Slice old_local_password,
td::Slice new_local_password) {
CHECK(secret_str.size() == 32);
td::SecureString old_local_password_hash(32);
sha256(old_local_password, old_local_password_hash.as_mutable_slice());
td::SecureString new_local_password_hash(32);
sha256(new_local_password, new_local_password_hash.as_mutable_slice());
td::SecureString new_secret(32);
for (size_t i = 0; i < new_secret.size(); i++) {
new_secret.as_mutable_slice()[i] =
secret_str[i] ^ old_local_password_hash.as_slice()[i] ^ new_local_password_hash.as_slice()[i];
}
return new_secret;
}
EncryptedKey DecryptedKey::encrypt(td::Slice local_password, td::Slice old_secret) const {
LOG(ERROR) << "encrypt";
td::SecureString secret(32);
if (old_secret.size() == td::as_slice(secret).size()) {
secret.as_mutable_slice().copy_from(old_secret);
} else {
td::Random::secure_bytes(secret.as_mutable_slice());
}
td::SecureString local_password_hash(32);
sha256(local_password, local_password_hash.as_mutable_slice());
td::SecureString decrypted_secret(32);
for (size_t i = 0; i < decrypted_secret.size(); i++) {
decrypted_secret.as_mutable_slice()[i] = secret.as_slice()[i] ^ local_password_hash.as_slice()[i];
}
hmac_sha256(secret, local_password, decrypted_secret.as_mutable_slice());
td::SecureString encryption_secret(64);
pbkdf2_sha512(as_slice(decrypted_secret), "TON local key", EncryptedKey::PBKDF_ITERATIONS,

View file

@ -56,8 +56,6 @@ struct DecryptedKey {
std::vector<td::SecureString> mnemonic_words;
td::Ed25519::PrivateKey private_key;
static td::SecureString change_local_password(td::Slice secret, td::Slice old_local_password,
td::Slice new_local_password);
EncryptedKey encrypt(td::Slice local_password, td::Slice secret = {}) const;
};

View file

@ -24,15 +24,19 @@
#include "td/utils/crypto.h"
namespace tonlib {
td::Result<DecryptedKey> EncryptedKey::decrypt(td::Slice local_password, bool check_public_key) {
td::Result<DecryptedKey> EncryptedKey::decrypt(td::Slice local_password, bool check_public_key, bool old) const {
if (secret.size() != 32) {
return td::Status::Error("Failed to decrypt key: invalid secret size");
}
td::SecureString local_password_hash(32);
sha256(local_password, local_password_hash.as_mutable_slice());
td::SecureString decrypted_secret(32);
for (size_t i = 0; i < 32; i++) {
decrypted_secret.as_mutable_slice()[i] = secret.as_slice()[i] ^ local_password_hash.as_slice()[i];
if (old) {
td::SecureString local_password_hash(32);
sha256(local_password, local_password_hash.as_mutable_slice());
for (size_t i = 0; i < 32; i++) {
decrypted_secret.as_mutable_slice()[i] = secret.as_slice()[i] ^ local_password_hash.as_slice()[i];
}
} else {
hmac_sha256(secret, local_password, decrypted_secret.as_mutable_slice());
}
td::SecureString encryption_secret(64);

View file

@ -32,7 +32,7 @@ struct EncryptedKey {
td::Ed25519::PublicKey public_key;
td::SecureString secret;
td::Result<DecryptedKey> decrypt(td::Slice local_password, bool check_public_key = true);
td::Result<DecryptedKey> decrypt(td::Slice local_password, bool check_public_key = true, bool old = false) const;
};
} // namespace tonlib

View file

@ -186,11 +186,21 @@ class TonlibCli : public td::actor::Actor {
if (cmd.empty()) {
return;
}
auto to_bool = [](td::Slice word, bool def = false) {
if (word.empty()) {
return def;
}
if (word == "0" || word == "FALSE" || word == "false") {
return false;
}
return true;
};
if (cmd == "help") {
td::TerminalIO::out() << "help - show this help\n";
td::TerminalIO::out() << "genkey - generate new secret key\n";
td::TerminalIO::out() << "keys - show all stored keys\n";
td::TerminalIO::out() << "unpackaddress <address> - validate and parse address\n";
td::TerminalIO::out() << "setbounceble <address> [<bounceble>] - change bounceble flag in address\n";
td::TerminalIO::out() << "importkey - import key\n";
td::TerminalIO::out() << "deletekeys - delete ALL PRIVATE KEYS\n";
td::TerminalIO::out() << "exportkey [<key_id>] - export key\n";
@ -214,6 +224,8 @@ class TonlibCli : public td::actor::Actor {
try_stop();
} else if (cmd == "keys") {
dump_keys();
} else if (cmd == "deletekey") {
//delete_key(parser.read_word());
} else if (cmd == "deletekeys") {
delete_all_keys();
} else if (cmd == "exportkey") {
@ -223,15 +235,6 @@ class TonlibCli : public td::actor::Actor {
} else if (cmd == "setconfig") {
auto config = parser.read_word();
auto name = parser.read_word();
auto to_bool = [](td::Slice word) {
if (word.empty()) {
return false;
}
if (word == "0" || word == "FALSE" || word == "false") {
return false;
}
return true;
};
auto use_callback = parser.read_word();
auto force = parser.read_word();
set_config(config, name, to_bool(use_callback), to_bool(force));
@ -251,6 +254,10 @@ class TonlibCli : public td::actor::Actor {
get_hints(parser.read_word());
} else if (cmd == "unpackaddress") {
unpack_address(parser.read_word());
} else if (cmd == "setbounceable") {
auto addr = parser.read_word();
auto bounceable = parser.read_word();
set_bounceable(addr, to_bool(bounceable, true));
}
}
@ -325,6 +332,26 @@ class TonlibCli : public td::actor::Actor {
});
}
void set_bounceable(td::Slice addr, bool bounceable) {
send_query(tonlib_api::make_object<tonlib_api::unpackAccountAddress>(addr.str()),
[addr = addr.str(), bounceable, this](auto r_parsed_addr) mutable {
if (r_parsed_addr.is_error()) {
LOG(ERROR) << "Failed to parse address: " << r_parsed_addr.error();
return;
}
auto parsed_addr = r_parsed_addr.move_as_ok();
parsed_addr->bounceable_ = bounceable;
this->send_query(tonlib_api::make_object<tonlib_api::packAccountAddress>(std::move(parsed_addr)),
[](auto r_addr) mutable {
if (r_addr.is_error()) {
LOG(ERROR) << "Failed to pack address";
return;
}
td::TerminalIO::out() << r_addr.ok()->account_address_ << "\n";
});
});
}
void generate_key(td::SecureString entropy = {}) {
if (entropy.size() < 20) {
td::TerminalIO::out() << "Enter some entropy";
@ -527,6 +554,25 @@ class TonlibCli : public td::actor::Actor {
return std::move(res);
}
void delete_key(td::Slice key) {
auto r_key_i = to_key_i(key);
if (r_key_i.is_error()) {
td::TerminalIO::out() << "Unknown key id: [" << key << "]\n";
return;
}
using tonlib_api::make_object;
auto key_i = r_key_i.move_as_ok();
send_query(make_object<tonlib_api::deleteKey>(
make_object<tonlib_api::key>(keys_[key_i].public_key, keys_[key_i].secret.copy())),
[key = key.str()](auto r_res) {
if (r_res.is_error()) {
td::TerminalIO::out() << "Can't delete key id: [" << key << "] " << r_res.error() << "\n";
return;
}
td::TerminalIO::out() << "Ok\n";
});
}
void export_key(td::Slice key) {
if (key.empty()) {
dump_keys();
@ -754,6 +800,11 @@ class TonlibCli : public td::actor::Actor {
}
void transfer(Address from, Address to, td::uint64 grams, td::Slice password, td::Slice message,
bool allow_send_to_uninited) {
auto r_sz = td::to_integer_safe<td::size_t>(message);
auto msg = message.str();
if (r_sz.is_ok()) {
msg = std::string(r_sz.ok(), 'Z');
}
using tonlib_api::make_object;
auto key = !from.secret.empty()
? make_object<tonlib_api::inputKey>(
@ -761,7 +812,7 @@ class TonlibCli : public td::actor::Actor {
: nullptr;
send_query(
make_object<tonlib_api::generic_sendGrams>(std::move(key), std::move(from.address), std::move(to.address),
grams, 30, allow_send_to_uninited, message.str()),
grams, 60, allow_send_to_uninited, std::move(msg)),
[this](auto r_res) {
if (r_res.is_error()) {
td::TerminalIO::out() << "Can't transfer: " << r_res.error() << "\n";
@ -881,6 +932,10 @@ int main(int argc, char* argv[]) {
options.config = std::move(data);
return td::Status::OK();
});
p.add_option('N', "config-name", "set lite server config name", [&](td::Slice arg) {
options.name = arg.str();
return td::Status::OK();
});
p.add_option('n', "use-callbacks-for-network", "do not use this", [&]() {
options.use_callbacks_for_network = true;
return td::Status::OK();