1
0
Fork 0
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:
ton 2020-02-06 21:56:46 +04:00
parent 53ec9684bd
commit 77842f9b63
128 changed files with 10555 additions and 2285 deletions

View file

@ -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 {

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -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");
}

View file

@ -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

View file

@ -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);

View file

@ -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();
});