mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
updated tonlib
- updated tonlib - updated validator - updated documentation - first version of http over rldp proxy
This commit is contained in:
parent
53ec9684bd
commit
77842f9b63
128 changed files with 10555 additions and 2285 deletions
|
@ -14,7 +14,7 @@
|
|||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
|
||||
#include "block/block.h"
|
||||
|
@ -60,6 +60,23 @@ TEST(Tonlib, CellString) {
|
|||
}
|
||||
};
|
||||
|
||||
TEST(Tonlib, Text) {
|
||||
for (unsigned size :
|
||||
{0, 1, 7, 8, 35, 127, 128, 255, 256, (int)vm::CellText::max_bytes - 1, (int)vm::CellText::max_bytes}) {
|
||||
auto str = td::rand_string('a', 'z', size);
|
||||
for (unsigned head : {16, 17, 127, 35 * 8, 127 * 8, 1023, 1024}) {
|
||||
vm::CellBuilder cb;
|
||||
vm::CellText::store(cb, str, head).ensure();
|
||||
auto cs = vm::load_cell_slice(cb.finalize());
|
||||
auto cs2 = cs;
|
||||
cs.print_rec(std::cerr);
|
||||
CHECK(block::gen::t_Text.validate_exact(cs2));
|
||||
auto got_str = vm::CellText::load(cs).move_as_ok();
|
||||
ASSERT_EQ(str, got_str);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using namespace tonlib;
|
||||
|
||||
TEST(Tonlib, PublicKey) {
|
||||
|
@ -126,7 +143,11 @@ TEST(Tonlib, InitClose) {
|
|||
)abc";
|
||||
|
||||
sync_send(client, make_object<tonlib_api::options_setConfig>(cfg(bad_config.str()))).ensure_error();
|
||||
sync_send(client, make_object<tonlib_api::testGiver_getAccountState>()).ensure_error();
|
||||
auto address =
|
||||
sync_send(client,
|
||||
make_object<tonlib_api::getAccountAddress>(make_object<tonlib_api::testGiver_initialAccountState>(), 0))
|
||||
.move_as_ok();
|
||||
sync_send(client, make_object<tonlib_api::getAccountState>(std::move(address))).ensure_error();
|
||||
sync_send(client, make_object<tonlib_api::close>()).ensure();
|
||||
sync_send(client, make_object<tonlib_api::close>()).ensure_error();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, dir("."))))
|
||||
|
@ -159,6 +180,38 @@ TEST(Tonlib, SimpleEncryption) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(Tonlib, SimpleEncryptionAsym) {
|
||||
auto private_key = td::Ed25519::generate_private_key().move_as_ok();
|
||||
auto public_key = private_key.get_public_key().move_as_ok();
|
||||
auto other_private_key = td::Ed25519::generate_private_key().move_as_ok();
|
||||
auto other_public_key = private_key.get_public_key().move_as_ok();
|
||||
auto wrong_private_key = td::Ed25519::generate_private_key().move_as_ok();
|
||||
{
|
||||
std::string data = "some private data";
|
||||
auto encrypted_data = SimpleEncryption::encrypt_data(data, public_key, other_private_key).move_as_ok();
|
||||
LOG(ERROR) << encrypted_data.size();
|
||||
auto decrypted_data = SimpleEncryption::decrypt_data(encrypted_data, private_key).move_as_ok();
|
||||
CHECK(data == decrypted_data);
|
||||
auto decrypted_data2 = SimpleEncryption::decrypt_data(encrypted_data, other_private_key).move_as_ok();
|
||||
CHECK(data == decrypted_data2);
|
||||
SimpleEncryption::decrypt_data(encrypted_data, wrong_private_key).ensure_error();
|
||||
SimpleEncryption::decrypt_data("", private_key).ensure_error();
|
||||
SimpleEncryption::decrypt_data(std::string(32, 'a'), private_key).ensure_error();
|
||||
SimpleEncryption::decrypt_data(std::string(33, 'a'), private_key).ensure_error();
|
||||
SimpleEncryption::decrypt_data(std::string(64, 'a'), private_key).ensure_error();
|
||||
SimpleEncryption::decrypt_data(std::string(128, 'a'), private_key).ensure_error();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 255; i++) {
|
||||
auto data = td::rand_string('a', 'z', static_cast<int>(i));
|
||||
auto encrypted_data = SimpleEncryption::encrypt_data(data, public_key, other_private_key).move_as_ok();
|
||||
auto decrypted_data = SimpleEncryption::decrypt_data(encrypted_data, private_key).move_as_ok();
|
||||
CHECK(data == decrypted_data);
|
||||
auto decrypted_data2 = SimpleEncryption::decrypt_data(encrypted_data, other_private_key).move_as_ok();
|
||||
CHECK(data == decrypted_data2);
|
||||
}
|
||||
}
|
||||
|
||||
class MnemonicBench : public td::Benchmark {
|
||||
public:
|
||||
std::string get_description() const override {
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
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-2019 Telegram Systems LLP
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
#include "adnl/adnl-ext-client.h"
|
||||
#include "tl-utils/tl-utils.hpp"
|
||||
|
@ -37,6 +37,7 @@
|
|||
#include "Ed25519.h"
|
||||
|
||||
#include "smc-envelope/GenericAccount.h"
|
||||
#include "smc-envelope/ManualDns.h"
|
||||
#include "smc-envelope/MultisigWallet.h"
|
||||
#include "smc-envelope/TestGiver.h"
|
||||
#include "smc-envelope/TestWallet.h"
|
||||
|
@ -108,6 +109,10 @@ struct Key {
|
|||
struct Wallet {
|
||||
std::string address;
|
||||
Key key;
|
||||
|
||||
auto get_address() const {
|
||||
return tonlib_api::make_object<tonlib_api::accountAddress>(address);
|
||||
}
|
||||
};
|
||||
|
||||
struct TransactionId {
|
||||
|
@ -116,7 +121,7 @@ struct TransactionId {
|
|||
};
|
||||
|
||||
struct AccountState {
|
||||
enum Type { Empty, Wallet, Unknown } type{Empty};
|
||||
enum Type { Empty, Wallet, Dns, Unknown } type{Empty};
|
||||
td::int64 sync_utime{-1};
|
||||
td::int64 balance{-1};
|
||||
TransactionId last_transaction_id;
|
||||
|
@ -136,8 +141,17 @@ void sync(Client& client) {
|
|||
static td::uint32 default_wallet_id{0};
|
||||
std::string wallet_address(Client& client, const Key& key) {
|
||||
return sync_send(client,
|
||||
make_object<tonlib_api::wallet_v3_getAccountAddress>(
|
||||
make_object<tonlib_api::wallet_v3_initialAccountState>(key.public_key, default_wallet_id)))
|
||||
make_object<tonlib_api::getAccountAddress>(
|
||||
make_object<tonlib_api::wallet_v3_initialAccountState>(key.public_key, default_wallet_id), 0))
|
||||
.move_as_ok()
|
||||
->account_address_;
|
||||
}
|
||||
|
||||
std::string highload_wallet_address(Client& client, const Key& key) {
|
||||
return sync_send(client, make_object<tonlib_api::getAccountAddress>(
|
||||
make_object<tonlib_api::wallet_highload_v2_initialAccountState>(key.public_key,
|
||||
default_wallet_id),
|
||||
1 /*TODO: guess revision!*/))
|
||||
.move_as_ok()
|
||||
->account_address_;
|
||||
}
|
||||
|
@ -148,35 +162,31 @@ Wallet import_wallet_from_pkey(Client& client, std::string pkey, std::string pas
|
|||
make_object<tonlib_api::exportedPemKey>(td::SecureString(pkey))))
|
||||
.move_as_ok();
|
||||
Wallet wallet{"", {key->public_key_, std::move(key->secret_)}};
|
||||
wallet.address = wallet_address(client, wallet.key);
|
||||
wallet.address = highload_wallet_address(client, wallet.key);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
std::string test_giver_address(Client& client) {
|
||||
using tonlib_api::make_object;
|
||||
return sync_send(client, make_object<tonlib_api::testGiver_getAccountAddress>()).move_as_ok()->account_address_;
|
||||
}
|
||||
|
||||
AccountState get_account_state(Client& client, std::string address) {
|
||||
auto generic_state = sync_send(client, tonlib_api::make_object<tonlib_api::generic_getAccountState>(
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(address)))
|
||||
.move_as_ok();
|
||||
auto state = sync_send(client, tonlib_api::make_object<tonlib_api::getAccountState>(
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(address)))
|
||||
.move_as_ok();
|
||||
AccountState res;
|
||||
tonlib_api::downcast_call(*generic_state, [&](auto& state) {
|
||||
res.balance = state.account_state_->balance_;
|
||||
res.sync_utime = state.account_state_->sync_utime_;
|
||||
res.last_transaction_id.lt = state.account_state_->last_transaction_id_->lt_;
|
||||
res.last_transaction_id.hash = state.account_state_->last_transaction_id_->hash_;
|
||||
});
|
||||
res.balance = state->balance_;
|
||||
res.sync_utime = state->sync_utime_;
|
||||
res.last_transaction_id.lt = state->last_transaction_id_->lt_;
|
||||
res.last_transaction_id.hash = state->last_transaction_id_->hash_;
|
||||
res.address = address;
|
||||
switch (generic_state->get_id()) {
|
||||
case tonlib_api::generic_accountStateUninited::ID:
|
||||
switch (state->account_state_->get_id()) {
|
||||
case tonlib_api::uninited_accountState::ID:
|
||||
res.type = AccountState::Empty;
|
||||
break;
|
||||
case tonlib_api::generic_accountStateWalletV3::ID:
|
||||
case tonlib_api::generic_accountStateWallet::ID:
|
||||
case tonlib_api::wallet_v3_accountState::ID:
|
||||
case tonlib_api::wallet_accountState::ID:
|
||||
res.type = AccountState::Wallet;
|
||||
break;
|
||||
case tonlib_api::dns_accountState::ID:
|
||||
res.type = AccountState::Dns;
|
||||
break;
|
||||
default:
|
||||
res.type = AccountState::Unknown;
|
||||
break;
|
||||
|
@ -219,13 +229,31 @@ struct QueryInfo {
|
|||
};
|
||||
|
||||
td::Result<QueryId> create_send_grams_query(Client& client, const Wallet& source, std::string destination,
|
||||
td::int64 amount, std::string message, bool force = false, int timeout = 0,
|
||||
bool fake = false) {
|
||||
auto r_id = sync_send(client, tonlib_api::make_object<tonlib_api::generic_createSendGramsQuery>(
|
||||
fake ? source.key.get_fake_input_key() : source.key.get_input_key(),
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(source.address),
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(destination), amount, timeout,
|
||||
force, std::move(message)));
|
||||
td::int64 amount, bool encrypted, std::string message, bool force = false,
|
||||
int timeout = 0, bool fake = false) {
|
||||
std::vector<tonlib_api::object_ptr<tonlib_api::msg_message>> msgs;
|
||||
tonlib_api::object_ptr<tonlib_api::msg_Data> data;
|
||||
if (encrypted) {
|
||||
data = tonlib_api::make_object<tonlib_api::msg_dataEncryptedText>(std::move(message));
|
||||
} else {
|
||||
data = tonlib_api::make_object<tonlib_api::msg_dataText>(std::move(message));
|
||||
}
|
||||
msgs.push_back(tonlib_api::make_object<tonlib_api::msg_message>(
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(destination), amount, std::move(data)));
|
||||
|
||||
auto r_id =
|
||||
sync_send(client, tonlib_api::make_object<tonlib_api::createQuery>(
|
||||
fake ? source.key.get_fake_input_key() : source.key.get_input_key(), source.get_address(),
|
||||
timeout, tonlib_api::make_object<tonlib_api::actionMsg>(std::move(msgs), force)));
|
||||
TRY_RESULT(id, std::move(r_id));
|
||||
return QueryId{id->id_};
|
||||
}
|
||||
|
||||
td::Result<QueryId> create_update_dns_query(Client& client, const Wallet& dns,
|
||||
std::vector<tonlib_api::object_ptr<tonlib_api::dns_Action>> entries) {
|
||||
using namespace ton::tonlib_api;
|
||||
auto r_id = sync_send(client, make_object<createQuery>(dns.key.get_input_key(), dns.get_address(), 60,
|
||||
make_object<actionDns>(std::move(entries))));
|
||||
TRY_RESULT(id, std::move(r_id));
|
||||
return QueryId{id->id_};
|
||||
}
|
||||
|
@ -242,7 +270,11 @@ td::Result<QueryId> create_raw_query(Client& client, std::string source, std::st
|
|||
std::pair<Fee, Fee> query_estimate_fees(Client& client, QueryId query_id, bool ignore_chksig = false) {
|
||||
auto fees = sync_send(client, tonlib_api::make_object<tonlib_api::query_estimateFees>(query_id.id, ignore_chksig))
|
||||
.move_as_ok();
|
||||
return std::make_pair(to_fee(fees->source_fees_), to_fee(fees->destination_fees_));
|
||||
Fee second;
|
||||
if (!fees->destination_fees_.empty()) {
|
||||
second = to_fee(fees->destination_fees_[0]);
|
||||
}
|
||||
return std::make_pair(to_fee(fees->source_fees_), second);
|
||||
}
|
||||
|
||||
void query_send(Client& client, QueryId query_id) {
|
||||
|
@ -266,26 +298,38 @@ td::Result<AccountState> wait_state_change(Client& client, const AccountState& o
|
|||
}
|
||||
};
|
||||
|
||||
td::Result<tonlib_api::object_ptr<tonlib_api::raw_transactions>> get_transactions(Client& client, std::string address,
|
||||
td::Result<tonlib_api::object_ptr<tonlib_api::raw_transactions>> get_transactions(Client& client,
|
||||
td::optional<const Wallet*> wallet,
|
||||
std::string address,
|
||||
const TransactionId& from) {
|
||||
auto got_transactions = sync_send(client, make_object<tonlib_api::raw_getTransactions>(
|
||||
wallet ? wallet.value()->key.get_input_key() : nullptr,
|
||||
make_object<tonlib_api::accountAddress>(address),
|
||||
make_object<tonlib_api::internal_transactionId>(from.lt, from.hash)))
|
||||
.move_as_ok();
|
||||
return std::move(got_transactions);
|
||||
}
|
||||
|
||||
td::Status transfer_grams(Client& client, const Wallet& wallet, std::string address, td::int64 amount) {
|
||||
td::Status transfer_grams(Client& client, const Wallet& wallet, std::string address, td::int64 amount,
|
||||
bool fast = false) {
|
||||
auto src_state = get_account_state(client, wallet.address);
|
||||
auto dst_state = get_account_state(client, address);
|
||||
auto message = td::rand_string('a', 'z', 500);
|
||||
|
||||
LOG(INFO) << "Transfer: create query " << (double)amount / Gramm << " from " << wallet.address << " to " << address;
|
||||
auto r_query_id = create_send_grams_query(client, wallet, address, amount, message);
|
||||
bool encrypt = true;
|
||||
auto r_query_id = create_send_grams_query(client, wallet, address, amount, encrypt, message, fast);
|
||||
if (r_query_id.is_error()) {
|
||||
LOG(INFO) << "Send query WITHOUT message encryption";
|
||||
encrypt = false;
|
||||
r_query_id = create_send_grams_query(client, wallet, address, amount, encrypt, message, fast);
|
||||
} else {
|
||||
LOG(INFO) << "Send query WITH message encryption";
|
||||
}
|
||||
if (r_query_id.is_error() && td::begins_with(r_query_id.error().message(), "DANGEROUS_TRANSACTION")) {
|
||||
ASSERT_TRUE(dst_state.type == AccountState::Empty);
|
||||
LOG(INFO) << "Transfer: recreate query due to DANGEROUS_TRANSACTION error";
|
||||
r_query_id = create_send_grams_query(client, wallet, address, amount, message, true);
|
||||
r_query_id = create_send_grams_query(client, wallet, address, amount, false, message, true);
|
||||
}
|
||||
|
||||
r_query_id.ensure();
|
||||
|
@ -304,6 +348,9 @@ td::Status transfer_grams(Client& client, const Wallet& wallet, std::string addr
|
|||
LOG(INFO) << "Transfer: send query";
|
||||
|
||||
query_send(client, query_id);
|
||||
if (fast) {
|
||||
return td::Status::OK();
|
||||
}
|
||||
td::Timer timer;
|
||||
TRY_RESULT(new_src_state, wait_state_change(client, src_state, query_info.valid_until));
|
||||
LOG(INFO) << "Transfer: reached source in " << timer;
|
||||
|
@ -311,7 +358,7 @@ td::Status transfer_grams(Client& client, const Wallet& wallet, std::string addr
|
|||
td::int64 lt;
|
||||
td::int64 first_fee;
|
||||
{
|
||||
auto tr = get_transactions(client, src_state.address, new_src_state.last_transaction_id).move_as_ok();
|
||||
auto tr = get_transactions(client, &wallet, src_state.address, new_src_state.last_transaction_id).move_as_ok();
|
||||
CHECK(tr->transactions_.size() > 0);
|
||||
const auto& txn = tr->transactions_[0];
|
||||
CHECK(txn->in_msg_->body_hash_ == query_info.body_hash);
|
||||
|
@ -331,7 +378,7 @@ td::Status transfer_grams(Client& client, const Wallet& wallet, std::string addr
|
|||
LOG(INFO) << "Transfer: reached destination in " << timer;
|
||||
|
||||
{
|
||||
auto tr = get_transactions(client, dst_state.address, new_dst_state.last_transaction_id).move_as_ok();
|
||||
auto tr = get_transactions(client, {}, dst_state.address, new_dst_state.last_transaction_id).move_as_ok();
|
||||
CHECK(tr->transactions_.size() > 0);
|
||||
const auto& txn = tr->transactions_[0];
|
||||
ASSERT_EQ(lt, txn->in_msg_->created_lt_);
|
||||
|
@ -341,7 +388,9 @@ td::Status transfer_grams(Client& client, const Wallet& wallet, std::string addr
|
|||
ASSERT_EQ(new_src_state.address, txn->in_msg_->source_);
|
||||
}
|
||||
ASSERT_EQ(new_src_state.address, txn->in_msg_->source_);
|
||||
ASSERT_EQ(message, txn->in_msg_->message_);
|
||||
if (!encrypt) {
|
||||
ASSERT_EQ(message, txn->in_msg_->message_);
|
||||
}
|
||||
auto fee_difference = fees.second.sum() - txn->fee_;
|
||||
auto desc = PSTRING() << fee_difference << " storage:[" << fees.second.storage_fee << " vs " << txn->storage_fee_
|
||||
<< "] other:[" << fees.second.sum() - fees.second.storage_fee << " vs " << txn->other_fee_
|
||||
|
@ -361,9 +410,10 @@ Wallet create_empty_wallet(Client& client) {
|
|||
Wallet wallet{"", {key->public_key_, std::move(key->secret_)}};
|
||||
|
||||
auto account_address =
|
||||
sync_send(client,
|
||||
make_object<tonlib_api::wallet_v3_getAccountAddress>(
|
||||
make_object<tonlib_api::wallet_v3_initialAccountState>(wallet.key.public_key, default_wallet_id)))
|
||||
sync_send(
|
||||
client,
|
||||
make_object<tonlib_api::getAccountAddress>(
|
||||
make_object<tonlib_api::wallet_v3_initialAccountState>(wallet.key.public_key, default_wallet_id), 0))
|
||||
.move_as_ok();
|
||||
|
||||
wallet.address = account_address->account_address_;
|
||||
|
@ -376,31 +426,33 @@ Wallet create_empty_wallet(Client& client) {
|
|||
return wallet;
|
||||
}
|
||||
|
||||
void dump_transaction_history(Client& client, std::string address) {
|
||||
Wallet create_empty_dns(Client& client) {
|
||||
using tonlib_api::make_object;
|
||||
auto state = sync_send(client, make_object<tonlib_api::testGiver_getAccountState>()).move_as_ok();
|
||||
auto tid = std::move(state->last_transaction_id_);
|
||||
int cnt = 0;
|
||||
while (tid->lt_ != 0) {
|
||||
auto lt = tid->lt_;
|
||||
auto got_transactions = sync_send(client, make_object<tonlib_api::raw_getTransactions>(
|
||||
make_object<tonlib_api::accountAddress>(address), std::move(tid)))
|
||||
.move_as_ok();
|
||||
CHECK(got_transactions->transactions_.size() > 0);
|
||||
CHECK(got_transactions->previous_transaction_id_->lt_ < lt);
|
||||
for (auto& txn : got_transactions->transactions_) {
|
||||
LOG(ERROR) << to_string(txn);
|
||||
cnt++;
|
||||
}
|
||||
tid = std::move(got_transactions->previous_transaction_id_);
|
||||
}
|
||||
LOG(ERROR) << cnt;
|
||||
auto key = sync_send(client, make_object<tonlib_api::createNewKey>(td::SecureString("local"), td::SecureString(),
|
||||
td::SecureString()))
|
||||
.move_as_ok();
|
||||
Wallet dns{"", {key->public_key_, std::move(key->secret_)}};
|
||||
|
||||
auto account_address =
|
||||
sync_send(client, make_object<tonlib_api::getAccountAddress>(
|
||||
make_object<tonlib_api::dns_initialAccountState>(dns.key.public_key, default_wallet_id), 0))
|
||||
.move_as_ok();
|
||||
|
||||
dns.address = account_address->account_address_;
|
||||
|
||||
// get state of empty account
|
||||
auto state = get_account_state(client, dns.address);
|
||||
ASSERT_EQ(-1, state.balance);
|
||||
ASSERT_EQ(AccountState::Empty, state.type);
|
||||
|
||||
return dns;
|
||||
}
|
||||
|
||||
void test_estimate_fees_without_key(Client& client, const Wallet& wallet_a, const Wallet& wallet_b) {
|
||||
LOG(ERROR) << " SUBTEST: estimate fees without key";
|
||||
{
|
||||
auto query_id = create_send_grams_query(client, wallet_a, wallet_b.address, 0, "???", true, 0, true).move_as_ok();
|
||||
auto query_id =
|
||||
create_send_grams_query(client, wallet_a, wallet_b.address, 0, false, "???", true, 0, true).move_as_ok();
|
||||
auto fees1 = query_estimate_fees(client, query_id, false);
|
||||
auto fees2 = query_estimate_fees(client, query_id, true);
|
||||
LOG(INFO) << "Fee without ignore_chksig\t" << fees1;
|
||||
|
@ -495,6 +547,74 @@ void test_multisig(Client& client, const Wallet& giver_wallet) {
|
|||
}
|
||||
}
|
||||
|
||||
void dns_resolve(Client& client, const Wallet& dns, std::string name) {
|
||||
using namespace ton::tonlib_api;
|
||||
auto address = dns.get_address();
|
||||
while (true) {
|
||||
auto resolved =
|
||||
sync_send(client, make_object<::ton::tonlib_api::dns_resolve>(std::move(address), name, 1)).move_as_ok();
|
||||
CHECK(resolved->entries_.size() == 1);
|
||||
LOG(INFO) << to_string(resolved);
|
||||
if (resolved->entries_[0]->category_ == -1) {
|
||||
auto entry = ton::move_tl_object_as<dns_entryDataNextResolver>(resolved->entries_[0]->entry_);
|
||||
address = std::move(entry->resolver_);
|
||||
continue;
|
||||
}
|
||||
LOG(INFO) << "OK";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void test_dns(Client& client, const Wallet& giver_wallet) {
|
||||
auto A = create_empty_dns(client);
|
||||
auto A_B = create_empty_dns(client);
|
||||
auto A_B_C = create_empty_dns(client);
|
||||
transfer_grams(client, giver_wallet, A.address, 1 * Gramm, true).ensure();
|
||||
transfer_grams(client, giver_wallet, A_B.address, 1 * Gramm, true).ensure();
|
||||
transfer_grams(client, giver_wallet, A_B_C.address, 1 * Gramm, true).ensure();
|
||||
|
||||
using namespace ton::tonlib_api;
|
||||
std::vector<object_ptr<dns_Action>> actions;
|
||||
|
||||
actions.push_back(make_object<dns_actionSet>(
|
||||
make_object<dns_entry>("A", -1, make_object<dns_entryDataNextResolver>(A_B.get_address()))));
|
||||
auto init_A = create_update_dns_query(client, A, std::move(actions)).move_as_ok();
|
||||
actions.push_back(make_object<dns_actionSet>(
|
||||
make_object<dns_entry>("B.A", -1, make_object<dns_entryDataNextResolver>(A_B_C.get_address()))));
|
||||
auto init_A_B = create_update_dns_query(client, A_B, std::move(actions)).move_as_ok();
|
||||
actions.push_back(
|
||||
make_object<dns_actionSet>(make_object<dns_entry>("C.B.A", 1, make_object<dns_entryDataText>("Hello dns"))));
|
||||
auto init_A_B_C = create_update_dns_query(client, A_B_C, std::move(actions)).move_as_ok();
|
||||
|
||||
LOG(INFO) << "Send dns init queries";
|
||||
::query_send(client, init_A);
|
||||
::query_send(client, init_A_B);
|
||||
::query_send(client, init_A_B_C);
|
||||
|
||||
auto wait = [&](auto& query, auto& dns) {
|
||||
auto info = query_get_info(client, query);
|
||||
LOG(INFO) << "Wait till dns " << dns.address << " is inited " << info.valid_until;
|
||||
while (true) {
|
||||
auto state = get_account_state(client, dns.address);
|
||||
LOG(INFO) << "Account type: " << state.type << " " << state.sync_utime;
|
||||
if (state.type == ::AccountState::Dns) {
|
||||
break;
|
||||
}
|
||||
if (state.sync_utime >= info.valid_until) {
|
||||
LOG(FATAL) << "Query expired";
|
||||
}
|
||||
LOG(INFO) << "time left to wait: " << td::format::as_time(info.valid_until - state.sync_utime);
|
||||
client.receive(1);
|
||||
}
|
||||
};
|
||||
wait(init_A, A);
|
||||
wait(init_A_B, A_B);
|
||||
wait(init_A_B_C, A_B_C);
|
||||
|
||||
// search "C.B.A"
|
||||
::dns_resolve(client, A, "C.B.A");
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
td::set_default_failure_signal_handler();
|
||||
using tonlib_api::make_object;
|
||||
|
@ -550,6 +670,7 @@ int main(int argc, char* argv[]) {
|
|||
// give wallet with some test grams to run test
|
||||
auto giver_wallet = import_wallet_from_pkey(client, giver_key_str, giver_key_pwd);
|
||||
|
||||
test_dns(client, giver_wallet);
|
||||
test_back_and_forth_transfer(client, giver_wallet, false);
|
||||
test_back_and_forth_transfer(client, giver_wallet, true);
|
||||
test_multisig(client, giver_wallet);
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include "td/actor/actor.h"
|
||||
|
@ -54,7 +54,7 @@ class LastConfig : public td::actor::Actor {
|
|||
QueryState get_config_state_{QueryState::Empty};
|
||||
|
||||
std::vector<td::Promise<LastConfigState>> promises_;
|
||||
std::vector<td::int32> params_{18, 20, 21, 24, 25};
|
||||
std::vector<td::int32> params_{4, 18, 20, 21, 24, 25};
|
||||
|
||||
void with_last_block(td::Result<LastBlockState> r_last_block);
|
||||
void on_config(td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_configInfo>> r_config);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -14,7 +14,7 @@
|
|||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
@ -38,6 +38,7 @@ namespace tonlib {
|
|||
namespace int_api {
|
||||
struct GetAccountState;
|
||||
struct GetPrivateKey;
|
||||
struct GetDnsResolver;
|
||||
struct SendMessage;
|
||||
inline std::string to_string(const int_api::SendMessage&) {
|
||||
return "Send message";
|
||||
|
@ -81,6 +82,10 @@ class TonlibClient : public td::actor::Actor {
|
|||
std::shared_ptr<KeyValue> kv_;
|
||||
KeyStorage key_storage_;
|
||||
LastBlockStorage last_block_storage_;
|
||||
struct QueryContext {
|
||||
td::optional<ton::BlockIdExt> block_id;
|
||||
};
|
||||
QueryContext query_context_;
|
||||
|
||||
// network
|
||||
td::actor::ActorOwn<ton::adnl::AdnlExtClient> raw_client_;
|
||||
|
@ -128,11 +133,7 @@ class TonlibClient : public td::actor::Actor {
|
|||
return tonlib_api::make_object<tonlib_api::error>(400, "Function can't be executed synchronously");
|
||||
}
|
||||
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::runTests& request);
|
||||
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::raw_getAccountAddress& request);
|
||||
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::testWallet_getAccountAddress& request);
|
||||
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::wallet_getAccountAddress& request);
|
||||
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::wallet_v3_getAccountAddress& request);
|
||||
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::testGiver_getAccountAddress& request);
|
||||
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::getAccountAddress& request);
|
||||
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::packAccountAddress& request);
|
||||
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::unpackAccountAddress& request);
|
||||
static object_ptr<tonlib_api::Object> do_static_request(tonlib_api::getBip39Hints& request);
|
||||
|
@ -153,15 +154,7 @@ class TonlibClient : public td::actor::Actor {
|
|||
template <class P>
|
||||
td::Status do_request(const tonlib_api::runTests& request, P&&);
|
||||
template <class P>
|
||||
td::Status do_request(const tonlib_api::raw_getAccountAddress& request, P&&);
|
||||
template <class P>
|
||||
td::Status do_request(const tonlib_api::testWallet_getAccountAddress& request, P&&);
|
||||
template <class P>
|
||||
td::Status do_request(const tonlib_api::wallet_getAccountAddress& request, P&&);
|
||||
template <class P>
|
||||
td::Status do_request(const tonlib_api::wallet_v3_getAccountAddress& request, P&&);
|
||||
template <class P>
|
||||
td::Status do_request(const tonlib_api::testGiver_getAccountAddress& request, P&&);
|
||||
td::Status do_request(const tonlib_api::getAccountAddress& request, P&&);
|
||||
template <class P>
|
||||
td::Status do_request(const tonlib_api::packAccountAddress& request, P&&);
|
||||
template <class P>
|
||||
|
@ -193,11 +186,15 @@ class TonlibClient : public td::actor::Actor {
|
|||
template <class P>
|
||||
td::Status do_request(const tonlib_api::kdf& request, P&&);
|
||||
|
||||
void make_any_request(tonlib_api::Function& function, QueryContext query_context,
|
||||
td::Promise<tonlib_api::object_ptr<tonlib_api::Object>>&& promise);
|
||||
template <class T, class P>
|
||||
void make_request(T&& request, P&& promise) {
|
||||
auto status = do_request(std::forward<T>(request), std::move(promise));
|
||||
td::Promise<typename std::decay_t<T>::ReturnType> new_promise = std::move(promise);
|
||||
|
||||
auto status = do_request(std::forward<T>(request), std::move(new_promise));
|
||||
if (status.is_error()) {
|
||||
promise.operator()(std::move(status));
|
||||
new_promise.operator()(std::move(status));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,33 +214,14 @@ class TonlibClient : public td::actor::Actor {
|
|||
td::Promise<object_ptr<tonlib_api::query_info>>&& promise);
|
||||
|
||||
td::Status do_request(tonlib_api::raw_getAccountState& request,
|
||||
td::Promise<object_ptr<tonlib_api::raw_accountState>>&& promise);
|
||||
td::Promise<object_ptr<tonlib_api::raw_fullAccountState>>&& promise);
|
||||
td::Status do_request(tonlib_api::raw_getTransactions& request,
|
||||
td::Promise<object_ptr<tonlib_api::raw_transactions>>&& promise);
|
||||
|
||||
td::Status do_request(const tonlib_api::testWallet_init& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Status do_request(const tonlib_api::testWallet_sendGrams& request,
|
||||
td::Promise<object_ptr<tonlib_api::sendGramsResult>>&& promise);
|
||||
td::Status do_request(tonlib_api::testWallet_getAccountState& request,
|
||||
td::Promise<object_ptr<tonlib_api::testWallet_accountState>>&& promise);
|
||||
td::Status do_request(const tonlib_api::getAccountState& request,
|
||||
td::Promise<object_ptr<tonlib_api::fullAccountState>>&& promise);
|
||||
|
||||
td::Status do_request(const tonlib_api::wallet_init& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Status do_request(const tonlib_api::wallet_sendGrams& request,
|
||||
td::Promise<object_ptr<tonlib_api::sendGramsResult>>&& promise);
|
||||
td::Status do_request(tonlib_api::wallet_getAccountState& request,
|
||||
td::Promise<object_ptr<tonlib_api::wallet_accountState>>&& promise);
|
||||
|
||||
td::Status do_request(const tonlib_api::testGiver_getAccountState& request,
|
||||
td::Promise<object_ptr<tonlib_api::testGiver_accountState>>&& promise);
|
||||
td::Status do_request(const tonlib_api::testGiver_sendGrams& request,
|
||||
td::Promise<object_ptr<tonlib_api::sendGramsResult>>&& promise);
|
||||
|
||||
td::Status do_request(const tonlib_api::generic_getAccountState& request,
|
||||
td::Promise<object_ptr<tonlib_api::generic_AccountState>>&& promise);
|
||||
td::Status do_request(tonlib_api::generic_sendGrams& request,
|
||||
td::Promise<object_ptr<tonlib_api::sendGramsResult>>&& promise);
|
||||
|
||||
td::Status do_request(tonlib_api::sync& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Status do_request(tonlib_api::sync& request, td::Promise<object_ptr<tonlib_api::ton_blockIdExt>>&& promise);
|
||||
|
||||
td::Status do_request(const tonlib_api::createNewKey& request, td::Promise<object_ptr<tonlib_api::key>>&& promise);
|
||||
td::Status do_request(const tonlib_api::exportKey& request,
|
||||
|
@ -275,8 +253,6 @@ class TonlibClient : public td::actor::Actor {
|
|||
td::Result<tonlib_api::object_ptr<tonlib_api::query_info>> get_query_info(td::int64 id);
|
||||
void finish_create_query(td::Result<td::unique_ptr<Query>> r_query,
|
||||
td::Promise<object_ptr<tonlib_api::query_info>>&& promise);
|
||||
void finish_send_query(td::Result<td::unique_ptr<Query>> r_query,
|
||||
td::Promise<object_ptr<tonlib_api::sendGramsResult>>&& promise);
|
||||
void query_estimate_fees(td::int64 id, bool ignore_chksig, td::Result<LastConfigState> r_state,
|
||||
td::Promise<object_ptr<tonlib_api::query_fees>>&& promise);
|
||||
|
||||
|
@ -287,8 +263,7 @@ class TonlibClient : public td::actor::Actor {
|
|||
td::Status do_request(const tonlib_api::query_send& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Status do_request(tonlib_api::query_forget& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
|
||||
td::Status do_request(tonlib_api::generic_createSendGramsQuery& request,
|
||||
td::Promise<object_ptr<tonlib_api::query_info>>&& promise);
|
||||
td::Status do_request(tonlib_api::createQuery& request, td::Promise<object_ptr<tonlib_api::query_info>>&& promise);
|
||||
|
||||
td::int64 next_smc_id_{0};
|
||||
std::map<td::int64, td::unique_ptr<AccountState>> smcs_;
|
||||
|
@ -307,13 +282,23 @@ class TonlibClient : public td::actor::Actor {
|
|||
td::Status do_request(const tonlib_api::smc_runGetMethod& request,
|
||||
td::Promise<object_ptr<tonlib_api::smc_runResult>>&& promise);
|
||||
|
||||
td::Status do_request(const tonlib_api::dns_resolve& request,
|
||||
td::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise);
|
||||
void do_dns_request(std::string name, td::int32 category, block::StdAddress address,
|
||||
td::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise);
|
||||
void finish_dns_resolve(std::string name, td::int32 category, td::unique_ptr<AccountState> smc,
|
||||
td::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise);
|
||||
|
||||
td::Status do_request(int_api::GetAccountState request, td::Promise<td::unique_ptr<AccountState>>&&);
|
||||
td::Status do_request(int_api::GetPrivateKey request, td::Promise<KeyStorage::PrivateKey>&&);
|
||||
td::Status do_request(int_api::GetDnsResolver request, td::Promise<block::StdAddress>&&);
|
||||
td::Status do_request(int_api::SendMessage request, td::Promise<td::Unit>&& promise);
|
||||
|
||||
td::Status do_request(const tonlib_api::liteServer_getInfo& request,
|
||||
td::Promise<object_ptr<tonlib_api::liteServer_info>>&& promise);
|
||||
|
||||
td::Status do_request(tonlib_api::withBlock& request, td::Promise<object_ptr<tonlib_api::Object>>&& promise);
|
||||
|
||||
void proxy_request(td::int64 query_id, std::string data);
|
||||
|
||||
friend class TonlibQueryActor;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
@ -39,6 +39,7 @@
|
|||
// ACCOUNT_NOT_INITED
|
||||
// ACCOUNT_TYPE_UNKNOWN
|
||||
// ACCOUNT_TYPE_UNEXPECTED
|
||||
// ACCOUNT_ACTION_UNSUPPORTED
|
||||
// VALIDATE_ACCOUNT_STATE
|
||||
// VALIDATE_TRANSACTION
|
||||
// VALIDATE_ZERO_STATE
|
||||
|
@ -86,7 +87,7 @@ struct TonlibError {
|
|||
return td::Status::Error(400, "MESSAGE_TOO_LONG");
|
||||
}
|
||||
static td::Status EmptyField(td::Slice field_name) {
|
||||
return td::Status::Error(400, PSLICE() << "EMPTY_FIELD: Field " << field_name << " must not be emtpy");
|
||||
return td::Status::Error(400, PSLICE() << "EMPTY_FIELD: Field " << field_name << " must not be empty");
|
||||
}
|
||||
static td::Status InvalidField(td::Slice field_name, td::Slice reason) {
|
||||
return td::Status::Error(400, PSLICE() << "INVALID_FIELD: Field " << field_name << " has invalid value " << reason);
|
||||
|
@ -103,6 +104,9 @@ struct TonlibError {
|
|||
static td::Status AccountTypeUnexpected(td::Slice expected) {
|
||||
return td::Status::Error(400, PSLICE() << "ACCOUNT_TYPE_UNEXPECTED: not a " << expected);
|
||||
}
|
||||
static td::Status AccountActionUnsupported(td::Slice action) {
|
||||
return td::Status::Error(400, PSLICE() << "ACCOUNT_ACTION_UNSUPPORTED: " << action);
|
||||
}
|
||||
static td::Status Internal() {
|
||||
return td::Status::Error(500, "INTERNAL");
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
#include "SimpleEncryption.h"
|
||||
|
||||
|
@ -36,6 +36,7 @@ td::AesCbcState SimpleEncryption::calc_aes_cbc_state_sha512(td::Slice seed) {
|
|||
sha512(seed, hash.as_mutable_slice());
|
||||
return calc_aes_cbc_state_hash(hash.as_slice());
|
||||
}
|
||||
|
||||
td::SecureString SimpleEncryption::gen_random_prefix(td::int64 data_size) {
|
||||
td::SecureString buff(td::narrow_cast<size_t>(((32 + 15 + data_size) & -16) - data_size), 0);
|
||||
td::Random::secure_bytes(buff.as_mutable_slice());
|
||||
|
@ -104,4 +105,46 @@ td::Result<td::SecureString> SimpleEncryption::decrypt_data(td::Slice encrypted_
|
|||
|
||||
return td::SecureString(decrypted_data.as_slice().substr(prefix_size));
|
||||
}
|
||||
|
||||
td::Result<td::SecureString> SimpleEncryption::encrypt_data(td::Slice data, const td::Ed25519::PublicKey &public_key) {
|
||||
TRY_RESULT(tmp_private_key, td::Ed25519::generate_private_key());
|
||||
return encrypt_data(data, public_key, tmp_private_key);
|
||||
}
|
||||
|
||||
namespace {
|
||||
td::SecureString secure_xor(td::Slice a, td::Slice b) {
|
||||
CHECK(a.size() == b.size());
|
||||
td::SecureString res(a.size());
|
||||
for (size_t i = 0; i < res.size(); i++) {
|
||||
res.as_mutable_slice()[i] = a[i] ^ b[i];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
td::Result<td::SecureString> SimpleEncryption::encrypt_data(td::Slice data, const td::Ed25519::PublicKey &public_key,
|
||||
const td::Ed25519::PrivateKey &private_key) {
|
||||
TRY_RESULT(shared_secret, td::Ed25519::compute_shared_secret(public_key, private_key));
|
||||
auto encrypted = encrypt_data(data, shared_secret.as_slice());
|
||||
TRY_RESULT(tmp_public_key, private_key.get_public_key());
|
||||
td::SecureString prefixed_encrypted(tmp_public_key.LENGTH + encrypted.size());
|
||||
prefixed_encrypted.as_mutable_slice().copy_from(tmp_public_key.as_octet_string());
|
||||
auto xored_keys = secure_xor(public_key.as_octet_string().as_slice(), tmp_public_key.as_octet_string().as_slice());
|
||||
prefixed_encrypted.as_mutable_slice().copy_from(xored_keys.as_slice());
|
||||
prefixed_encrypted.as_mutable_slice().substr(xored_keys.size()).copy_from(encrypted);
|
||||
return std::move(prefixed_encrypted);
|
||||
}
|
||||
|
||||
td::Result<td::SecureString> SimpleEncryption::decrypt_data(td::Slice data,
|
||||
const td::Ed25519::PrivateKey &private_key) {
|
||||
if (data.size() < td::Ed25519::PublicKey::LENGTH) {
|
||||
return td::Status::Error("Failed to decrypte: data is too small");
|
||||
}
|
||||
TRY_RESULT(public_key, private_key.get_public_key());
|
||||
auto tmp_public_key =
|
||||
td::Ed25519::PublicKey(secure_xor(data.substr(0, td::Ed25519::PublicKey::LENGTH), public_key.as_octet_string()));
|
||||
TRY_RESULT(shared_secret, td::Ed25519::compute_shared_secret(tmp_public_key, private_key));
|
||||
TRY_RESULT(decrypted, decrypt_data(data.substr(td::Ed25519::PublicKey::LENGTH), shared_secret.as_slice()));
|
||||
return std::move(decrypted);
|
||||
}
|
||||
} // namespace tonlib
|
||||
|
|
|
@ -14,13 +14,14 @@
|
|||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/crypto.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/SharedSlice.h"
|
||||
#include "crypto/Ed25519.h"
|
||||
|
||||
namespace tonlib {
|
||||
class SimpleEncryption {
|
||||
|
@ -30,6 +31,11 @@ class SimpleEncryption {
|
|||
static td::SecureString combine_secrets(td::Slice a, td::Slice b);
|
||||
static td::SecureString kdf(td::Slice secret, td::Slice password, int iterations);
|
||||
|
||||
static td::Result<td::SecureString> encrypt_data(td::Slice data, const td::Ed25519::PublicKey &public_key);
|
||||
static td::Result<td::SecureString> decrypt_data(td::Slice data, const td::Ed25519::PrivateKey &private_key);
|
||||
static td::Result<td::SecureString> encrypt_data(td::Slice data, const td::Ed25519::PublicKey &public_key,
|
||||
const td::Ed25519::PrivateKey &private_key);
|
||||
|
||||
private:
|
||||
static td::AesCbcState calc_aes_cbc_state_hash(td::Slice hash);
|
||||
static td::AesCbcState calc_aes_cbc_state_sha512(td::Slice seed);
|
||||
|
|
|
@ -1,3 +1,30 @@
|
|||
/*
|
||||
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 2019-2020 Telegram Systems LLP
|
||||
*/
|
||||
#include "td/actor/actor.h"
|
||||
|
||||
#include "td/utils/filesystem.h"
|
||||
|
@ -16,62 +43,65 @@
|
|||
|
||||
#include "tonlib/ExtClientLazy.h"
|
||||
|
||||
#include "smc-envelope/ManualDns.h"
|
||||
|
||||
#include "auto/tl/tonlib_api.hpp"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
// Consider this as a TODO list:
|
||||
//
|
||||
// (from lite-client)
|
||||
// SUPPORTED
|
||||
// "time\tGet server time\n"
|
||||
// "remote-version\tShows server time, version and capabilities\n"
|
||||
// "help [<command>]\tThis help\n" // TODO: support [<command>]
|
||||
// "quit\tExit\n";
|
||||
// "sendfile <filename>\tLoad a serialized message from <filename> and send it to server\n"
|
||||
// "saveaccount[code|data] <filename> <addr> [<block-id-ext>]\tSaves into specified file the most recent state "
|
||||
// "(StateInit) or just the code or data of specified account; <addr> is in "
|
||||
// "[<workchain>:]<hex-or-base64-addr> format\n"
|
||||
//
|
||||
// "runmethod <addr> <method-id> <params>...\tRuns GET method <method-id> of account <addr> "
|
||||
// "with specified parameters\n"
|
||||
//
|
||||
// "getaccount <addr> [<block-id-ext>]\tLoads the most recent state of specified account; <addr> is in "
|
||||
// "[<workchain>:]<hex-or-base64-addr> format\n"
|
||||
//
|
||||
// WONTSUPPORT
|
||||
//
|
||||
// UNSUPPORTED
|
||||
//"last\tGet last block and state info from server\n"
|
||||
//"status\tShow connection and local database status\n"
|
||||
//"allshards [<block-id-ext>]\tShows shard configuration from the most recent masterchain "
|
||||
//"state or from masterchain state corresponding to <block-id-ext>\n"
|
||||
//"getconfig [<param>...]\tShows specified or all configuration parameters from the latest masterchain state\n"
|
||||
//"getconfigfrom <block-id-ext> [<param>...]\tShows specified or all configuration parameters from the "
|
||||
//"masterchain state of <block-id-ext>\n"
|
||||
//"saveconfig <filename> [<block-id-ext>]\tSaves all configuration parameters into specified file\n"
|
||||
//"gethead <block-id-ext>\tShows block header for <block-id-ext>\n"
|
||||
//"getblock <block-id-ext>\tDownloads block\n"
|
||||
//"dumpblock <block-id-ext>\tDownloads and dumps specified block\n"
|
||||
//"getstate <block-id-ext>\tDownloads state corresponding to specified block\n"
|
||||
//"dumpstate <block-id-ext>\tDownloads and dumps state corresponding to specified block\n"
|
||||
//"dumptrans <block-id-ext> <account-id> <trans-lt>\tDumps one transaction of specified account\n"
|
||||
//"lasttrans[dump] <account-id> <trans-lt> <trans-hash> [<count>]\tShows or dumps specified transaction and "
|
||||
//"several preceding "
|
||||
//"ones\n"
|
||||
//"listblocktrans[rev] <block-id-ext> <count> [<start-account-id> <start-trans-lt>]\tLists block transactions, "
|
||||
//"starting immediately after or before the specified one\n"
|
||||
//"blkproofchain[step] <from-block-id-ext> [<to-block-id-ext>]\tDownloads and checks proof of validity of the /"second "
|
||||
//"indicated block (or the last known masterchain block) starting from given block\n"
|
||||
//"byseqno <workchain> <shard-prefix> <seqno>\tLooks up a block by workchain, shard and seqno, and shows its "
|
||||
//"header\n"
|
||||
//"bylt <workchain> <shard-prefix> <lt>\tLooks up a block by workchain, shard and logical time, and shows its "
|
||||
//"header\n"
|
||||
//"byutime <workchain> <shard-prefix> <utime>\tLooks up a block by workchain, shard and creation time, and "
|
||||
//"shows its header\n"
|
||||
//"known\tShows the list of all known block ids\n"
|
||||
//"privkey <filename>\tLoads a private key from file\n"
|
||||
// GR$<amount>
|
||||
struct Grams {
|
||||
td::uint64 nano;
|
||||
};
|
||||
|
||||
td::StringBuilder& operator<<(td::StringBuilder& sb, const Grams& grams) {
|
||||
auto b = grams.nano % 1000000000;
|
||||
auto a = grams.nano / 1000000000;
|
||||
sb << "GR$" << a;
|
||||
if (b != 0) {
|
||||
size_t sz = 9;
|
||||
while (b % 10 == 0) {
|
||||
sz--;
|
||||
b /= 10;
|
||||
}
|
||||
sb << '.';
|
||||
[&](auto b_str) {
|
||||
for (size_t i = b_str.size(); i < sz; i++) {
|
||||
sb << '0';
|
||||
}
|
||||
sb << b_str;
|
||||
}(PSLICE() << b);
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
td::Result<Grams> parse_grams(td::Slice grams) {
|
||||
td::ConstParser parser(grams);
|
||||
if (parser.skip_start_with("GR$")) {
|
||||
TRY_RESULT(a, td::to_integer_safe<td::uint32>(parser.read_till_nofail('.')));
|
||||
td::uint64 res = a;
|
||||
if (parser.try_skip('.')) {
|
||||
for (int i = 0; i < 9; i++) {
|
||||
res *= 10;
|
||||
if (parser.peek_char() >= '0' && parser.peek_char() <= '9') {
|
||||
res += parser.peek_char() - '0';
|
||||
parser.advance(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res *= 1000000000;
|
||||
}
|
||||
if (!parser.empty()) {
|
||||
return td::Status::Error(PSLICE() << "Failed to parse grams \"" << grams << "\", left \"" << parser.read_all()
|
||||
<< "\"");
|
||||
}
|
||||
return Grams{res};
|
||||
}
|
||||
TRY_RESULT(value, td::to_integer_safe<td::uint64>(grams));
|
||||
return Grams{value};
|
||||
}
|
||||
|
||||
class TonlibCli : public td::actor::Actor {
|
||||
public:
|
||||
|
@ -83,6 +113,7 @@ class TonlibCli : public td::actor::Actor {
|
|||
bool in_memory{false};
|
||||
bool use_callbacks_for_network{false};
|
||||
td::int32 wallet_version = 2;
|
||||
td::int32 wallet_revision = 0;
|
||||
bool ignore_cache{false};
|
||||
|
||||
bool one_shot{false};
|
||||
|
@ -98,6 +129,8 @@ class TonlibCli : public td::actor::Actor {
|
|||
std::uint64_t next_query_id_{1};
|
||||
td::Promise<td::Slice> cont_;
|
||||
td::uint32 wallet_id_;
|
||||
ton::tonlib_api::object_ptr<tonlib_api::ton_blockIdExt> current_block_;
|
||||
enum class BlockMode { Auto, Manual } block_mode_ = BlockMode::Auto;
|
||||
|
||||
struct KeyInfo {
|
||||
std::string public_key;
|
||||
|
@ -189,13 +222,15 @@ class TonlibCli : public td::actor::Actor {
|
|||
[&](auto r_ok) {
|
||||
LOG_IF(ERROR, r_ok.is_error()) << r_ok.error();
|
||||
if (r_ok.is_ok()) {
|
||||
wallet_id_ = static_cast<td::uint32>(r_ok.ok()->config_info_->default_wallet_id_);
|
||||
if (r_ok.ok()->config_info_) {
|
||||
wallet_id_ = static_cast<td::uint32>(r_ok.ok()->config_info_->default_wallet_id_);
|
||||
}
|
||||
td::TerminalIO::out() << "Tonlib is inited\n";
|
||||
if (options_.one_shot) {
|
||||
td::actor::send_closure(actor_id(this), &TonlibCli::parse_line, td::BufferSlice(options_.cmd));
|
||||
}
|
||||
}
|
||||
});
|
||||
if (options_.one_shot) {
|
||||
td::actor::send_closure(actor_id(this), &TonlibCli::parse_line, td::BufferSlice(options_.cmd));
|
||||
}
|
||||
}
|
||||
|
||||
void hangup_shared() override {
|
||||
|
@ -222,19 +257,6 @@ class TonlibCli : public td::actor::Actor {
|
|||
}
|
||||
}
|
||||
|
||||
void on_error() {
|
||||
if (options_.one_shot) {
|
||||
LOG(ERROR) << "FAILED";
|
||||
std::_Exit(1);
|
||||
}
|
||||
}
|
||||
void on_ok() {
|
||||
if (options_.one_shot) {
|
||||
LOG(INFO) << "OK";
|
||||
std::_Exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
void parse_line(td::BufferSlice line) {
|
||||
if (is_closing_) {
|
||||
return;
|
||||
|
@ -259,11 +281,18 @@ class TonlibCli : public td::actor::Actor {
|
|||
return true;
|
||||
};
|
||||
|
||||
td::Promise<td::Unit> cmd_promise = [line = line.clone()](td::Result<td::Unit> res) {
|
||||
td::Promise<td::Unit> cmd_promise = [line = line.clone(), one_shot = options_.one_shot](td::Result<td::Unit> res) {
|
||||
if (res.is_ok()) {
|
||||
// on_ok
|
||||
if (one_shot) {
|
||||
LOG(DEBUG) << "OK";
|
||||
std::_Exit(0);
|
||||
}
|
||||
} else {
|
||||
td::TerminalIO::out() << "Query {" << line.as_slice() << "} FAILED: \n\t" << res.error() << "\n";
|
||||
if (one_shot) {
|
||||
LOG(ERROR) << "FAILED";
|
||||
std::_Exit(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -274,6 +303,23 @@ class TonlibCli : public td::actor::Actor {
|
|||
td::TerminalIO::out() << "sendfile <filename>\tLoad a serialized message from <filename> and send it to server\n";
|
||||
td::TerminalIO::out() << "setconfig|validateconfig <path> [<name>] [<use_callback>] [<force>] - set or validate "
|
||||
"lite server config\n";
|
||||
td::TerminalIO::out() << "runmethod <addr> <method-id> <params>...\tRuns GET method <method-id> of account "
|
||||
"<addr> with specified parameters\n";
|
||||
td::TerminalIO::out() << "getstate <key_id>\tget state of wallet with requested key\n";
|
||||
td::TerminalIO::out() << "getaddress <key_id>\tget address of wallet with requested key\n";
|
||||
td::TerminalIO::out() << "dns resove <addr> <name>\n";
|
||||
td::TerminalIO::out() << "dns cmd <key_id> <dns_cmd>\n";
|
||||
//td::TerminalIO::out() << "dns cmdlist <key_id> {<dns_cmd>\\n} end\n";
|
||||
td::TerminalIO::out() << "dns cmdfile <key_id> <file>\n";
|
||||
td::TerminalIO::out() << "\t<dns_cmd> = set <name> <category> <data> | delete.name <name> | delete.all\n";
|
||||
td::TerminalIO::out() << "\t<data> = DELETED | EMPTY | TEXT:<text>\n";
|
||||
|
||||
td::TerminalIO::out()
|
||||
<< "blockmode auto|manual\tWith auto mode, all queries will be executed with respect to the latest block. "
|
||||
"With manual mode, user must update current block explicitly: with last or setblock\n";
|
||||
td::TerminalIO::out() << "last\tUpdate current block to the most recent one\n";
|
||||
td::TerminalIO::out() << "setblock <block>\tSet current block\n";
|
||||
|
||||
td::TerminalIO::out() << "exit\tExit\n";
|
||||
td::TerminalIO::out() << "quit\tExit\n";
|
||||
td::TerminalIO::out()
|
||||
|
@ -284,10 +330,10 @@ class TonlibCli : public td::actor::Actor {
|
|||
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() << "importkeypem <filename> - import key\n";
|
||||
td::TerminalIO::out() << "deletekeys - delete ALL PRIVATE KEYS\n";
|
||||
td::TerminalIO::out() << "exportkey [<key_id>] - export key\n";
|
||||
td::TerminalIO::out() << "exportkeypem [<key_id>] - export key\n";
|
||||
td::TerminalIO::out() << "getstate <key_id> - get state of simple wallet with requested key\n";
|
||||
td::TerminalIO::out()
|
||||
<< "gethistory <key_id> - get history fo simple wallet with requested key (last 10 transactions)\n";
|
||||
td::TerminalIO::out() << "init <key_id> - init simple wallet with requested key\n";
|
||||
|
@ -312,18 +358,6 @@ class TonlibCli : public td::actor::Actor {
|
|||
export_key(cmd.str(), parser.read_word());
|
||||
} else if (cmd == "importkey") {
|
||||
import_key(parser.read_all());
|
||||
} else if (cmd == "getstate") {
|
||||
get_state(parser.read_word());
|
||||
} else if (cmd == "gethistory") {
|
||||
get_history(parser.read_word());
|
||||
} else if (cmd == "init") {
|
||||
init_simple_wallet(parser.read_word());
|
||||
} else if (cmd == "transfer" || cmd == "transferf") {
|
||||
auto from = parser.read_word();
|
||||
auto to = parser.read_word();
|
||||
auto grams = parser.read_word();
|
||||
auto message = parser.read_word();
|
||||
transfer(from, to, grams, message, cmd == "transferf");
|
||||
} else if (cmd == "hint") {
|
||||
get_hints(parser.read_word());
|
||||
} else if (cmd == "unpackaddress") {
|
||||
|
@ -335,8 +369,10 @@ class TonlibCli : public td::actor::Actor {
|
|||
} else if (cmd == "netstats") {
|
||||
dump_netstats();
|
||||
// reviewed from here
|
||||
} else if (cmd == "sync") {
|
||||
sync(std::move(cmd_promise));
|
||||
} else if (cmd == "blockmode") {
|
||||
set_block_mode(parser.read_word(), std::move(cmd_promise));
|
||||
} else if (cmd == "sync" || cmd == "last") {
|
||||
sync(std::move(cmd_promise), cmd == "last");
|
||||
} else if (cmd == "time") {
|
||||
remote_time(std::move(cmd_promise));
|
||||
} else if (cmd == "remote-version") {
|
||||
|
@ -355,6 +391,22 @@ class TonlibCli : public td::actor::Actor {
|
|||
auto use_callback = parser.read_word();
|
||||
auto force = parser.read_word();
|
||||
set_validate_config(cmd, config, name, to_bool(use_callback), to_bool(force), std::move(cmd_promise));
|
||||
} else if (td::begins_with(cmd, "transfer") || cmd == "init") {
|
||||
// transfer[f][F]
|
||||
// f - force
|
||||
// F from file - SEND <address> <amount> <message>
|
||||
// use @empty for empty message
|
||||
transfer(parser, cmd, std::move(cmd_promise));
|
||||
} else if (cmd == "getstate") {
|
||||
get_state(parser.read_word(), std::move(cmd_promise));
|
||||
} else if (cmd == "getaddress") {
|
||||
get_address(parser.read_word(), std::move(cmd_promise));
|
||||
} else if (cmd == "importkeypem") {
|
||||
import_key_pem(parser.read_word(), std::move(cmd_promise));
|
||||
} else if (cmd == "dns") {
|
||||
run_dns_cmd(parser, std::move(cmd_promise));
|
||||
} else if (cmd == "gethistory") {
|
||||
get_history(parser.read_word(), std::move(cmd_promise));
|
||||
} else {
|
||||
cmd_promise.set_error(td::Status::Error(PSLICE() << "Unkwnown query `" << cmd << "`"));
|
||||
}
|
||||
|
@ -363,6 +415,112 @@ class TonlibCli : public td::actor::Actor {
|
|||
}
|
||||
}
|
||||
|
||||
void run_dns_cmd(td::ConstParser& parser, td::Promise<td::Unit> promise) {
|
||||
auto cmd = parser.read_word();
|
||||
if (cmd == "cmd" || cmd == "cmdlist" || cmd == "cmdfile") {
|
||||
return dns_cmd(cmd, parser, std::move(promise));
|
||||
}
|
||||
if (cmd == "resolve") {
|
||||
return dns_resolve(parser, std::move(promise));
|
||||
}
|
||||
promise.set_error(td::Status::Error("Unknown cmd"));
|
||||
}
|
||||
|
||||
void do_dns_resolve(std::string name, td::int16 category, td::int32 ttl,
|
||||
tonlib_api::object_ptr<tonlib_api::dns_resolved> resolved, td::Promise<td::Unit> promise) {
|
||||
if (resolved->entries_.empty()) {
|
||||
td::TerminalIO::out() << "No dns entries found\n";
|
||||
promise.set_value(td::Unit());
|
||||
return;
|
||||
}
|
||||
if (resolved->entries_[0]->name_ == name) {
|
||||
td::TerminalIO::out() << "Done: " << to_string(resolved);
|
||||
promise.set_value(td::Unit());
|
||||
return;
|
||||
}
|
||||
if (resolved->entries_[0]->entry_->get_id() == tonlib_api::dns_entryDataNextResolver::ID) {
|
||||
auto entry = tonlib_api::move_object_as<tonlib_api::dns_entryDataNextResolver>(resolved->entries_[0]->entry_);
|
||||
send_query(tonlib_api::make_object<tonlib_api::dns_resolve>(std::move(entry->resolver_), name, category),
|
||||
promise.send_closure(actor_id(this), &TonlibCli::do_dns_resolve, name, category, ttl));
|
||||
}
|
||||
promise.set_error(td::Status::Error("Failed to resolve"));
|
||||
}
|
||||
|
||||
void dns_resolve(td::ConstParser& parser, td::Promise<td::Unit> promise) {
|
||||
auto key_id = parser.read_word();
|
||||
TRY_RESULT_PROMISE(promise, address, to_account_address(key_id, false));
|
||||
auto name = parser.read_word();
|
||||
auto category_str = parser.read_word();
|
||||
TRY_RESULT_PROMISE(promise, category, td::to_integer_safe<td::int16>(category_str));
|
||||
|
||||
std::vector<tonlib_api::object_ptr<tonlib_api::dns_entry>> entries;
|
||||
entries.push_back(tonlib_api::make_object<tonlib_api::dns_entry>(
|
||||
"", -1, tonlib_api::make_object<tonlib_api::dns_entryDataNextResolver>(std::move(address.address))));
|
||||
do_dns_resolve(name.str(), category, 10, tonlib_api::make_object<tonlib_api::dns_resolved>(std::move(entries)),
|
||||
std::move(promise));
|
||||
}
|
||||
void dns_cmd(td::Slice cmd, td::ConstParser& parser, td::Promise<td::Unit> promise) {
|
||||
auto key_id = parser.read_word();
|
||||
TRY_RESULT_PROMISE(promise, address, to_account_address(key_id, true));
|
||||
|
||||
std::vector<ton::ManualDns::ActionExt> actions_ext;
|
||||
if (cmd == "cmd") {
|
||||
TRY_RESULT_PROMISE_ASSIGN(promise, actions_ext, ton::ManualDns::parse(parser.read_all()));
|
||||
} else if (cmd == "cmdfile") {
|
||||
TRY_RESULT_PROMISE(promise, file_data, td::read_file(parser.read_word().str()));
|
||||
TRY_RESULT_PROMISE_ASSIGN(promise, actions_ext, ton::ManualDns::parse(file_data));
|
||||
}
|
||||
|
||||
std::vector<tonlib_api::object_ptr<tonlib_api::dns_Action>> actions;
|
||||
for (auto& action : actions_ext) {
|
||||
if (action.name.empty()) {
|
||||
actions.push_back(tonlib_api::make_object<tonlib_api::dns_actionDeleteAll>());
|
||||
td::TerminalIO::out() << "Delete all dns entries\n";
|
||||
} else if (action.category == 0) {
|
||||
actions.push_back(tonlib_api::make_object<tonlib_api::dns_actionDelete>(action.name, 0));
|
||||
td::TerminalIO::out() << "Delete all dns enties with name: " << action.name << "\n";
|
||||
} else if (!action.data) {
|
||||
actions.push_back(tonlib_api::make_object<tonlib_api::dns_actionDelete>(action.name, action.category));
|
||||
td::TerminalIO::out() << "Delete all dns enties with name and category: " << action.name << ":"
|
||||
<< action.category << "\n";
|
||||
} else {
|
||||
tonlib_api::object_ptr<tonlib_api::dns_EntryData> data;
|
||||
td::StringBuilder sb;
|
||||
|
||||
td::Status error;
|
||||
if (action.data.value().data.empty()) {
|
||||
TRY_STATUS_PROMISE(promise, td::Status::Error("Empty entry data is not supported"));
|
||||
}
|
||||
action.data.value().data.visit(td::overloaded(
|
||||
[&](const ton::ManualDns::EntryDataText& text) {
|
||||
data = tonlib_api::make_object<tonlib_api::dns_entryDataText>(text.text);
|
||||
sb << "TEXT:" << text.text;
|
||||
},
|
||||
[&](const ton::ManualDns::EntryDataNextResolver& resolver) { error = td::Status::Error("TODO"); },
|
||||
[&](const ton::ManualDns::EntryDataAdnlAddress& adnl_address) { error = td::Status::Error("TODO"); },
|
||||
[&](const ton::ManualDns::EntryDataSmcAddress& text) { error = td::Status::Error("TODO"); }));
|
||||
;
|
||||
TRY_STATUS_PROMISE(promise, std::move(error));
|
||||
td::TerminalIO::out() << "Set dns entry: " << action.name << ":" << action.category << " " << sb.as_cslice()
|
||||
<< "\n";
|
||||
actions.push_back(tonlib_api::make_object<tonlib_api::dns_actionSet>(
|
||||
tonlib_api::make_object<tonlib_api::dns_entry>(action.name, action.category, std::move(data))));
|
||||
}
|
||||
}
|
||||
|
||||
auto action = tonlib_api::make_object<tonlib_api::actionDns>(std::move(actions));
|
||||
|
||||
td::Slice password; // empty by default
|
||||
using tonlib_api::make_object;
|
||||
auto key = !address.secret.empty() ? make_object<tonlib_api::inputKeyRegular>(
|
||||
make_object<tonlib_api::key>(address.public_key, address.secret.copy()),
|
||||
td::SecureString(password))
|
||||
: nullptr;
|
||||
send_query(tonlib_api::make_object<tonlib_api::createQuery>(std::move(key), std::move(address.address), 60,
|
||||
std::move(action)),
|
||||
promise.send_closure(actor_id(this), &TonlibCli::transfer2));
|
||||
}
|
||||
|
||||
void remote_time(td::Promise<td::Unit> promise) {
|
||||
send_query(tonlib_api::make_object<tonlib_api::liteServer_getInfo>(), promise.wrap([](auto&& info) {
|
||||
td::TerminalIO::out() << "Lite server time is: " << info->now_ << "\n";
|
||||
|
@ -421,13 +579,29 @@ class TonlibCli : public td::actor::Actor {
|
|||
promise.set_value(td::Unit());
|
||||
}
|
||||
|
||||
void sync(td::Promise<td::Unit> promise) {
|
||||
void sync(td::Promise<td::Unit> promise, bool update_last) {
|
||||
using tonlib_api::make_object;
|
||||
send_query(make_object<tonlib_api::sync>(), promise.wrap([](auto&&) {
|
||||
send_query(make_object<tonlib_api::sync>(), promise.wrap([&, update_last](auto&& block) {
|
||||
td::TerminalIO::out() << "synchronized\n";
|
||||
td::TerminalIO::out() << to_string(block) << "\n";
|
||||
if (update_last) {
|
||||
current_block_ = std::move(block);
|
||||
td::TerminalIO::out() << "Update current block\n";
|
||||
}
|
||||
return td::Unit();
|
||||
}));
|
||||
}
|
||||
void set_block_mode(td::Slice mode, td::Promise<td::Unit> promise) {
|
||||
if (mode == "auto") {
|
||||
block_mode_ = BlockMode::Auto;
|
||||
promise.set_value(td::Unit());
|
||||
} else if (mode == "manual") {
|
||||
block_mode_ = BlockMode::Manual;
|
||||
promise.set_value(td::Unit());
|
||||
} else {
|
||||
promise.set_error(td::Status::Error("Invalid block mode"));
|
||||
}
|
||||
}
|
||||
td::Result<tonlib_api::object_ptr<tonlib_api::tvm_StackEntry>> parse_stack_entry(td::Slice str) {
|
||||
if (str.empty() || str.size() > 65535) {
|
||||
return td::Status::Error("String is or empty or too big");
|
||||
|
@ -691,8 +865,20 @@ class TonlibCli : public td::actor::Actor {
|
|||
if (is_closing_) {
|
||||
return;
|
||||
}
|
||||
tonlib_api::object_ptr<tonlib_api::Function> func = std::move(query);
|
||||
if (block_mode_ == BlockMode::Manual && func->get_id() != tonlib_api::sync::ID) {
|
||||
if (!current_block_) {
|
||||
promise.set_error(td::Status::Error("empty current block"));
|
||||
return;
|
||||
}
|
||||
func = tonlib_api::make_object<tonlib_api::withBlock>(
|
||||
tonlib_api::make_object<tonlib_api::ton_blockIdExt>(current_block_->workchain_, current_block_->shard_,
|
||||
current_block_->seqno_, current_block_->root_hash_,
|
||||
current_block_->file_hash_),
|
||||
std::move(func));
|
||||
}
|
||||
auto query_id = next_query_id_++;
|
||||
td::actor::send_closure(client_, &tonlib::TonlibClient::request, query_id, std::move(query));
|
||||
td::actor::send_closure(client_, &tonlib::TonlibClient::request, query_id, std::move(func));
|
||||
query_handlers_[query_id] =
|
||||
[promise = std::move(promise)](td::Result<tonlib_api::object_ptr<tonlib_api::Object>> r_obj) mutable {
|
||||
if (r_obj.is_error()) {
|
||||
|
@ -808,7 +994,6 @@ class TonlibCli : public td::actor::Actor {
|
|||
KeyInfo info;
|
||||
info.public_key = public_key;
|
||||
info.secret = r_secret.move_as_ok();
|
||||
LOG(INFO) << info.public_key;
|
||||
|
||||
keys_.push_back(std::move(info));
|
||||
}
|
||||
|
@ -900,22 +1085,39 @@ class TonlibCli : public td::actor::Actor {
|
|||
if (key.empty()) {
|
||||
return td::Status::Error("account address is empty");
|
||||
}
|
||||
if (key == "none" && !need_private_key) {
|
||||
return Address{};
|
||||
}
|
||||
auto r_key_i = to_key_i(key);
|
||||
using tonlib_api::make_object;
|
||||
if (r_key_i.is_ok()) {
|
||||
auto obj = [&](td::int32 version) {
|
||||
auto obj = [&](td::int32 version, td::int32 revision) {
|
||||
auto do_request = [revision](auto x) {
|
||||
return tonlib::TonlibClient::static_request(
|
||||
make_object<tonlib_api::getAccountAddress>(std::move(x), revision));
|
||||
};
|
||||
if (version == 1) {
|
||||
return tonlib::TonlibClient::static_request(make_object<tonlib_api::testWallet_getAccountAddress>(
|
||||
make_object<tonlib_api::testWallet_initialAccountState>(keys_[r_key_i.ok()].public_key)));
|
||||
return do_request(make_object<tonlib_api::testWallet_initialAccountState>(keys_[r_key_i.ok()].public_key));
|
||||
}
|
||||
if (version == 2) {
|
||||
return tonlib::TonlibClient::static_request(make_object<tonlib_api::wallet_getAccountAddress>(
|
||||
make_object<tonlib_api::wallet_initialAccountState>(keys_[r_key_i.ok()].public_key)));
|
||||
return do_request(make_object<tonlib_api::wallet_initialAccountState>(keys_[r_key_i.ok()].public_key));
|
||||
}
|
||||
return tonlib::TonlibClient::static_request(make_object<tonlib_api::wallet_v3_getAccountAddress>(
|
||||
make_object<tonlib_api::wallet_v3_initialAccountState>(keys_[r_key_i.ok()].public_key, wallet_id_)));
|
||||
if (version == 4) {
|
||||
return do_request(make_object<tonlib_api::wallet_highload_v1_initialAccountState>(
|
||||
keys_[r_key_i.ok()].public_key, wallet_id_));
|
||||
}
|
||||
if (version == 5) {
|
||||
return do_request(make_object<tonlib_api::wallet_highload_v2_initialAccountState>(
|
||||
keys_[r_key_i.ok()].public_key, wallet_id_));
|
||||
}
|
||||
if (version == 6) {
|
||||
return do_request(
|
||||
make_object<tonlib_api::dns_initialAccountState>(keys_[r_key_i.ok()].public_key, wallet_id_));
|
||||
}
|
||||
return do_request(
|
||||
make_object<tonlib_api::wallet_v3_initialAccountState>(keys_[r_key_i.ok()].public_key, wallet_id_));
|
||||
UNREACHABLE();
|
||||
}(options_.wallet_version);
|
||||
}(options_.wallet_version, options_.wallet_revision);
|
||||
if (obj->get_id() != tonlib_api::error::ID) {
|
||||
Address res;
|
||||
res.address = ton::move_tl_object_as<tonlib_api::accountAddress>(obj);
|
||||
|
@ -925,7 +1127,8 @@ class TonlibCli : public td::actor::Actor {
|
|||
}
|
||||
}
|
||||
if (key == "giver") {
|
||||
auto obj = tonlib::TonlibClient::static_request(make_object<tonlib_api::testGiver_getAccountAddress>());
|
||||
auto obj = tonlib::TonlibClient::static_request(
|
||||
make_object<tonlib_api::getAccountAddress>(make_object<tonlib_api::testGiver_initialAccountState>(), 0));
|
||||
if (obj->get_id() != tonlib_api::error::ID) {
|
||||
Address res;
|
||||
res.address = ton::move_tl_object_as<tonlib_api::accountAddress>(obj);
|
||||
|
@ -983,6 +1186,23 @@ class TonlibCli : public td::actor::Actor {
|
|||
cont_ = [this, cmd, key = key.str(), key_i](td::Slice password) { this->export_key(cmd, key, key_i, password); };
|
||||
}
|
||||
|
||||
void import_key_pem(td::Slice filename, td::Promise<td::Unit> promise) {
|
||||
TRY_RESULT_PROMISE(promise, data, td::read_file_secure(filename.str()));
|
||||
using tonlib_api::make_object;
|
||||
send_query(make_object<tonlib_api::importPemKey>(td::SecureString(), td::SecureString("cucumber"),
|
||||
make_object<tonlib_api::exportedPemKey>(std::move(data))),
|
||||
promise.wrap([&](auto&& key) {
|
||||
LOG(ERROR) << to_string(key);
|
||||
KeyInfo info;
|
||||
info.public_key = key->public_key_;
|
||||
info.secret = std::move(key->secret_);
|
||||
keys_.push_back(std::move(info));
|
||||
export_key("exportkey", key->public_key_, keys_.size() - 1, td::SecureString());
|
||||
store_keys();
|
||||
return td::Unit();
|
||||
}));
|
||||
}
|
||||
|
||||
void export_key(std::string cmd, std::string key, size_t key_i, td::Slice password) {
|
||||
using tonlib_api::make_object;
|
||||
if (cmd == "exportkey") {
|
||||
|
@ -1038,7 +1258,7 @@ class TonlibCli : public td::actor::Actor {
|
|||
|
||||
void import_key(std::vector<td::SecureString> words, td::Slice password) {
|
||||
using tonlib_api::make_object;
|
||||
send_query(make_object<tonlib_api::importKey>(td::SecureString(password), td::SecureString(" test mnemonic"),
|
||||
send_query(make_object<tonlib_api::importKey>(td::SecureString(password), td::SecureString(""),
|
||||
make_object<tonlib_api::exportedKey>(std::move(words))),
|
||||
[this, password = td::SecureString(password)](auto r_res) {
|
||||
if (r_res.is_error()) {
|
||||
|
@ -1056,226 +1276,149 @@ class TonlibCli : public td::actor::Actor {
|
|||
});
|
||||
}
|
||||
|
||||
void get_state(td::Slice key) {
|
||||
if (key.empty()) {
|
||||
dump_keys();
|
||||
td::TerminalIO::out() << "Choose public key (hex prefix or #N)";
|
||||
cont_ = [this](td::Slice key) { this->get_state(key); };
|
||||
on_wait();
|
||||
return;
|
||||
}
|
||||
auto r_address = to_account_address(key, false);
|
||||
if (r_address.is_error()) {
|
||||
td::TerminalIO::out() << "Unknown key id: [" << key << "]\n";
|
||||
on_error();
|
||||
return;
|
||||
}
|
||||
auto address = r_address.move_as_ok();
|
||||
void get_state(td::Slice key, td::Promise<td::Unit> promise) {
|
||||
TRY_RESULT_PROMISE(promise, address, to_account_address(key, false));
|
||||
using tonlib_api::make_object;
|
||||
send_query(make_object<tonlib_api::generic_getAccountState>(
|
||||
auto address_str = address.address->account_address_;
|
||||
send_query(make_object<tonlib_api::getAccountState>(
|
||||
ton::move_tl_object_as<tonlib_api::accountAddress>(std::move(address.address))),
|
||||
[this](auto r_res) {
|
||||
if (r_res.is_error()) {
|
||||
td::TerminalIO::out() << "Can't get state: " << r_res.error() << "\n";
|
||||
on_error();
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << to_string(r_res.ok());
|
||||
on_ok();
|
||||
});
|
||||
promise.wrap([address_str](auto&& state) {
|
||||
td::TerminalIO::out() << "Address: " << address_str << "\n";
|
||||
td::TerminalIO::out() << "Balance: "
|
||||
<< Grams{td::narrow_cast<td::uint64>(state->balance_ * (state->balance_ > 0))}
|
||||
<< "\n";
|
||||
td::TerminalIO::out() << "Sync utime: " << state->sync_utime_ << "\n";
|
||||
td::TerminalIO::out() << "transaction.LT: " << state->last_transaction_id_->lt_ << "\n";
|
||||
td::TerminalIO::out() << "transaction.Hash: " << td::base64_encode(state->last_transaction_id_->hash_)
|
||||
<< "\n";
|
||||
td::TerminalIO::out() << to_string(state->account_state_);
|
||||
return td::Unit();
|
||||
}));
|
||||
}
|
||||
void get_history(td::Slice key) {
|
||||
if (key.empty()) {
|
||||
dump_keys();
|
||||
td::TerminalIO::out() << "Choose public key (hex prefix or #N)";
|
||||
cont_ = [this](td::Slice key) { this->get_state(key); };
|
||||
return;
|
||||
}
|
||||
auto r_address = to_account_address(key, false);
|
||||
if (r_address.is_error()) {
|
||||
td::TerminalIO::out() << "Unknown key id: [" << key << "]\n";
|
||||
return;
|
||||
}
|
||||
auto address = r_address.move_as_ok();
|
||||
|
||||
void get_address(td::Slice key, td::Promise<td::Unit> promise) {
|
||||
TRY_RESULT_PROMISE(promise, address, to_account_address(key, false));
|
||||
promise.set_value(td::Unit());
|
||||
td::TerminalIO::out() << address.address->account_address_ << "\n";
|
||||
}
|
||||
|
||||
void get_history(td::Slice key, td::Promise<td::Unit> promise) {
|
||||
TRY_RESULT_PROMISE(promise, address, to_account_address(key, false));
|
||||
using tonlib_api::make_object;
|
||||
send_query(make_object<tonlib_api::generic_getAccountState>(
|
||||
send_query(make_object<tonlib_api::getAccountState>(
|
||||
ton::move_tl_object_as<tonlib_api::accountAddress>(std::move(address.address))),
|
||||
[this, key = key.str()](auto r_res) {
|
||||
if (r_res.is_error()) {
|
||||
td::TerminalIO::out() << "Can't get state: " << r_res.error() << "\n";
|
||||
return;
|
||||
}
|
||||
this->get_history(key, *r_res.move_as_ok());
|
||||
});
|
||||
promise.send_closure(td::actor::actor_id(this), &TonlibCli::get_history2, key.str()));
|
||||
}
|
||||
|
||||
void get_history(td::Slice key, tonlib_api::generic_AccountState& state) {
|
||||
auto r_address = to_account_address(key, false);
|
||||
void get_history2(td::Slice key, td::Result<tonlib_api::object_ptr<tonlib_api::fullAccountState>> r_state,
|
||||
td::Promise<td::Unit> promise) {
|
||||
TRY_RESULT_PROMISE(promise, state, std::move(r_state));
|
||||
auto r_address = to_account_address(key, true);
|
||||
if (r_address.is_error()) {
|
||||
td::TerminalIO::out() << "Unknown key id: [" << key << "]\n";
|
||||
return;
|
||||
r_address = to_account_address(key, false);
|
||||
}
|
||||
auto address = r_address.move_as_ok();
|
||||
TRY_RESULT_PROMISE(promise, address, std::move(r_address));
|
||||
td::Slice password;
|
||||
using tonlib_api::make_object;
|
||||
auto input_key =
|
||||
!address.secret.empty()
|
||||
? make_object<tonlib_api::inputKeyRegular>(
|
||||
make_object<tonlib_api::key>(address.public_key, address.secret.copy()), td::SecureString(password))
|
||||
: nullptr;
|
||||
|
||||
tonlib_api::object_ptr<tonlib_api::internal_transactionId> transaction_id;
|
||||
downcast_call(state, [&](auto& state) { transaction_id = std::move(state.account_state_->last_transaction_id_); });
|
||||
|
||||
send_query(
|
||||
tonlib_api::make_object<tonlib_api::raw_getTransactions>(
|
||||
ton::move_tl_object_as<tonlib_api::accountAddress>(std::move(address.address)), std::move(transaction_id)),
|
||||
[](auto r_res) {
|
||||
if (r_res.is_error()) {
|
||||
td::TerminalIO::out() << "Can't get transactions: " << r_res.error() << "\n";
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << to_string(r_res.move_as_ok()) << "\n";
|
||||
});
|
||||
send_query(tonlib_api::make_object<tonlib_api::raw_getTransactions>(
|
||||
std::move(input_key), ton::move_tl_object_as<tonlib_api::accountAddress>(std::move(address.address)),
|
||||
std::move(state->last_transaction_id_)),
|
||||
promise.wrap([](auto res) {
|
||||
td::TerminalIO::out() << to_string(res) << "\n";
|
||||
return td::Unit();
|
||||
}));
|
||||
}
|
||||
|
||||
void transfer(td::Slice from, td::Slice to, td::Slice grams, td::Slice message, bool allow_send_to_uninited) {
|
||||
auto r_from_address = to_account_address(from, true);
|
||||
if (r_from_address.is_error()) {
|
||||
td::TerminalIO::out() << "Unknown key id: [" << from << "] : " << r_from_address.error() << "\n";
|
||||
on_error();
|
||||
return;
|
||||
void transfer(td::ConstParser& parser, td::Slice cmd, td::Promise<td::Unit> cmd_promise) {
|
||||
bool from_file = false;
|
||||
bool force = false;
|
||||
if (cmd != "init") {
|
||||
td::ConstParser cmd_parser(cmd);
|
||||
cmd_parser.advance(td::Slice("transfer").size());
|
||||
while (!cmd_parser.empty()) {
|
||||
auto c = cmd_parser.peek_char();
|
||||
cmd_parser.advance(1);
|
||||
if (c == 'F') {
|
||||
from_file = true;
|
||||
} else if (c == 'f') {
|
||||
force = true;
|
||||
} else {
|
||||
cmd_promise.set_error(td::Status::Error(PSLICE() << "Unknown suffix '" << c << "'"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto r_to_address = to_account_address(to, false);
|
||||
if (r_to_address.is_error()) {
|
||||
td::TerminalIO::out() << "Unknown key id: [" << to << "] : " << r_to_address.error() << "\n";
|
||||
on_error();
|
||||
return;
|
||||
}
|
||||
auto r_grams = td::to_integer_safe<td::uint64>(grams);
|
||||
if (r_grams.is_error()) {
|
||||
td::TerminalIO::out() << "Invalid grams amount: [" << grams << "]\n";
|
||||
on_error();
|
||||
return;
|
||||
}
|
||||
if (options_.one_shot) {
|
||||
transfer(r_from_address.move_as_ok(), r_to_address.move_as_ok(), r_grams.move_as_ok(), "", "",
|
||||
allow_send_to_uninited);
|
||||
return;
|
||||
}
|
||||
if (from != "giver" && message.empty()) {
|
||||
td::TerminalIO::out() << "Enter password (could be empty)";
|
||||
cont_ = [this, from = r_from_address.move_as_ok(), to = r_to_address.move_as_ok(), grams = r_grams.move_as_ok(),
|
||||
allow_send_to_uninited](td::Slice password) mutable {
|
||||
this->transfer(std::move(from), std::move(to), grams, password, allow_send_to_uninited);
|
||||
};
|
||||
on_wait();
|
||||
return;
|
||||
}
|
||||
if (message.empty()) {
|
||||
transfer(r_from_address.move_as_ok(), r_to_address.move_as_ok(), r_grams.move_as_ok(), "",
|
||||
allow_send_to_uninited);
|
||||
} else {
|
||||
transfer(r_from_address.move_as_ok(), r_to_address.move_as_ok(), r_grams.move_as_ok(), "", message,
|
||||
allow_send_to_uninited);
|
||||
}
|
||||
}
|
||||
|
||||
void transfer(Address from, Address to, td::uint64 grams, td::Slice password, bool allow_send_to_uninited) {
|
||||
td::TerminalIO::out() << "Enter message (could be empty)";
|
||||
cont_ = [this, from = std::move(from), to = std::move(to), grams, password = password.str(),
|
||||
allow_send_to_uninited](td::Slice message) mutable {
|
||||
this->transfer(std::move(from), std::move(to), grams, password, message, allow_send_to_uninited);
|
||||
auto from = parser.read_word();
|
||||
TRY_RESULT_PROMISE(cmd_promise, from_address, to_account_address(from, true));
|
||||
|
||||
struct Message {
|
||||
Address to;
|
||||
td::int64 amount;
|
||||
std::string message;
|
||||
};
|
||||
on_wait();
|
||||
return;
|
||||
}
|
||||
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<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::inputKeyRegular>(
|
||||
make_object<tonlib_api::key>(from.public_key, from.secret.copy()), td::SecureString(password))
|
||||
: nullptr;
|
||||
send_query(make_object<tonlib_api::generic_createSendGramsQuery>(std::move(key), std::move(from.address),
|
||||
std::move(to.address), grams, 60,
|
||||
allow_send_to_uninited, std::move(msg)),
|
||||
[self = this](auto r_res) {
|
||||
if (r_res.is_error()) {
|
||||
td::TerminalIO::out() << "Can't transfer: " << r_res.error() << "\n";
|
||||
self->on_error();
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << to_string(r_res.ok());
|
||||
self->send_query(make_object<tonlib_api::query_estimateFees>(r_res.ok()->id_, false),
|
||||
[self](auto r_res) {
|
||||
if (r_res.is_error()) {
|
||||
td::TerminalIO::out() << "Can't transfer: " << r_res.error() << "\n";
|
||||
self->on_error();
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << to_string(r_res.ok());
|
||||
//self->on_ok();
|
||||
});
|
||||
|
||||
self->send_query(make_object<tonlib_api::query_send>(r_res.ok()->id_), [self](auto r_res) {
|
||||
if (r_res.is_error()) {
|
||||
td::TerminalIO::out() << "Can't transfer: " << r_res.error() << "\n";
|
||||
self->on_error();
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << to_string(r_res.ok());
|
||||
self->on_ok();
|
||||
});
|
||||
std::vector<tonlib_api::object_ptr<tonlib_api::msg_message>> messages;
|
||||
auto add_message = [&](td::ConstParser& parser) {
|
||||
auto to = parser.read_word();
|
||||
auto grams = parser.read_word();
|
||||
auto message = parser.read_word();
|
||||
|
||||
//self->on_ok();
|
||||
});
|
||||
}
|
||||
Message res;
|
||||
TRY_RESULT(address, to_account_address(to, false));
|
||||
TRY_RESULT(amount, parse_grams(grams));
|
||||
messages.push_back(tonlib_api::make_object<tonlib_api::msg_message>(
|
||||
std::move(address.address), amount.nano, tonlib_api::make_object<tonlib_api::msg_dataText>(message.str())));
|
||||
return td::Status::OK();
|
||||
};
|
||||
|
||||
void init_simple_wallet(td::Slice key) {
|
||||
if (key.empty()) {
|
||||
dump_keys();
|
||||
td::TerminalIO::out() << "Choose public key (hex prefix or #N)";
|
||||
cont_ = [this](td::Slice key) { this->init_simple_wallet(key); };
|
||||
return;
|
||||
}
|
||||
auto r_key_i = to_key_i(key);
|
||||
if (r_key_i.is_error()) {
|
||||
td::TerminalIO::out() << "Unknown key id: [" << key << "]\n";
|
||||
return;
|
||||
}
|
||||
auto key_i = r_key_i.move_as_ok();
|
||||
|
||||
td::TerminalIO::out() << "Key #" << key_i << "\n"
|
||||
<< "public key: " << td::buffer_to_hex(keys_[key_i].public_key) << "\n";
|
||||
|
||||
td::TerminalIO::out() << "Enter password (could be empty)";
|
||||
cont_ = [this, key = key.str(), key_i](td::Slice password) { this->init_simple_wallet(key, key_i, password); };
|
||||
}
|
||||
|
||||
void init_simple_wallet(std::string key, size_t key_i, td::Slice password) {
|
||||
using tonlib_api::make_object;
|
||||
if (options_.wallet_version == 1) {
|
||||
send_query(make_object<tonlib_api::testWallet_init>(make_object<tonlib_api::inputKeyRegular>(
|
||||
make_object<tonlib_api::key>(keys_[key_i].public_key, keys_[key_i].secret.copy()),
|
||||
td::SecureString(password))),
|
||||
[key = std::move(key)](auto r_res) {
|
||||
if (r_res.is_error()) {
|
||||
td::TerminalIO::out() << "Can't init wallet with key: [" << key << "] " << r_res.error() << "\n";
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << to_string(r_res.ok());
|
||||
});
|
||||
if (from_file) {
|
||||
TRY_RESULT_PROMISE(cmd_promise, data, td::read_file(parser.read_word().str()));
|
||||
auto lines = td::full_split(data.as_slice(), '\n');
|
||||
for (auto& line : lines) {
|
||||
td::ConstParser parser(line);
|
||||
parser.skip_whitespaces();
|
||||
if (parser.empty()) {
|
||||
continue;
|
||||
}
|
||||
if (parser.read_word() != "SEND") {
|
||||
TRY_STATUS_PROMISE(cmd_promise, td::Status::Error("Expected `SEND` in file"));
|
||||
}
|
||||
TRY_STATUS_PROMISE(cmd_promise, add_message(parser));
|
||||
}
|
||||
} else {
|
||||
send_query(make_object<tonlib_api::wallet_init>(make_object<tonlib_api::inputKeyRegular>(
|
||||
make_object<tonlib_api::key>(keys_[key_i].public_key, keys_[key_i].secret.copy()),
|
||||
td::SecureString(password))),
|
||||
[key = std::move(key)](auto r_res) {
|
||||
if (r_res.is_error()) {
|
||||
td::TerminalIO::out() << "Can't init wallet with key: [" << key << "] " << r_res.error() << "\n";
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << to_string(r_res.ok());
|
||||
});
|
||||
while (parser.skip_whitespaces(), !parser.empty()) {
|
||||
TRY_STATUS_PROMISE(cmd_promise, add_message(parser));
|
||||
}
|
||||
}
|
||||
|
||||
td::Slice password; // empty by default
|
||||
using tonlib_api::make_object;
|
||||
auto key = !from_address.secret.empty()
|
||||
? make_object<tonlib_api::inputKeyRegular>(
|
||||
make_object<tonlib_api::key>(from_address.public_key, from_address.secret.copy()),
|
||||
td::SecureString(password))
|
||||
: nullptr;
|
||||
|
||||
bool allow_send_to_uninited = force;
|
||||
|
||||
send_query(make_object<tonlib_api::createQuery>(
|
||||
std::move(key), std::move(from_address.address), 60,
|
||||
make_object<tonlib_api::actionMsg>(std::move(messages), allow_send_to_uninited)),
|
||||
cmd_promise.send_closure(actor_id(this), &TonlibCli::transfer2));
|
||||
}
|
||||
|
||||
void transfer2(td::Result<tonlib_api::object_ptr<tonlib_api::query_info>> r_info, td::Promise<td::Unit> cmd_promise) {
|
||||
send_query(tonlib_api::make_object<tonlib_api::query_send>(r_info.ok()->id_), cmd_promise.wrap([](auto&& info) {
|
||||
td::TerminalIO::out() << "Transfer sent!\n";
|
||||
return td::Unit();
|
||||
}));
|
||||
}
|
||||
|
||||
void get_hints(td::Slice prefix) {
|
||||
|
@ -1346,8 +1489,17 @@ int main(int argc, char* argv[]) {
|
|||
options.use_callbacks_for_network = true;
|
||||
return td::Status::OK();
|
||||
});
|
||||
p.add_option('W', "wallet-version", "do not use this", [&](td::Slice arg) {
|
||||
options.wallet_version = td::to_integer<td::int32>(arg);
|
||||
p.add_option('W', "wallet-version", "do not use this (version[.revision])", [&](td::Slice arg) {
|
||||
td::ConstParser parser(arg);
|
||||
TRY_RESULT(version, td::to_integer_safe<td::int32>((parser.read_till_nofail('.'))));
|
||||
options.wallet_version = version;
|
||||
LOG(INFO) << "Use wallet version = " << version;
|
||||
if (parser.peek_char() == '.') {
|
||||
parser.skip('.');
|
||||
TRY_RESULT(revision, td::to_integer_safe<td::int32>((parser.read_all())));
|
||||
options.wallet_revision = revision;
|
||||
LOG(INFO) << "Use wallet revision = " << revision;
|
||||
}
|
||||
return td::Status::OK();
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue