mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
updated tonlib, block routing
- upated tonlib - fixed bug in message routing
This commit is contained in:
parent
ac3eb1a7b8
commit
fd7a8de970
33 changed files with 1002 additions and 381 deletions
|
@ -91,6 +91,35 @@ INC NEWC 32 STU 256 STU ENDC c4 POPCTR
|
|||
return fift::compile_asm(code).move_as_ok();
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> get_wallet_source() {
|
||||
std::string code = R"ABCD(
|
||||
SETCP0 DUP IFNOTRET // return if recv_internal
|
||||
DUP 85143 INT EQUAL IFJMP:<{ // "seqno" get-method
|
||||
DROP c4 PUSHCTR CTOS 32 PLDU // cnt
|
||||
}>
|
||||
INC 32 THROWIF // fail unless recv_external
|
||||
9 PUSHPOW2 LDSLICEX DUP 32 LDU 32 LDU // signature in_msg msg_seqno valid_until cs
|
||||
SWAP NOW LEQ 35 THROWIF // signature in_msg msg_seqno cs
|
||||
c4 PUSH CTOS 32 LDU 256 LDU ENDS // signature in_msg msg_seqno cs stored_seqno public_key
|
||||
s3 s1 XCPU // signature in_msg public_key cs stored_seqno msg_seqno stored_seqno
|
||||
EQUAL 33 THROWIFNOT // signature in_msg public_key cs stored_seqno
|
||||
s0 s3 XCHG HASHSU // signature stored_seqno public_key cs hash
|
||||
s0 s4 s2 XC2PU CHKSIGNU 34 THROWIFNOT // cs stored_seqno public_key
|
||||
ACCEPT
|
||||
s0 s2 XCHG // public_key stored_seqno cs
|
||||
WHILE:<{
|
||||
DUP SREFS // public_key stored_seqno cs _40
|
||||
}>DO<{ // public_key stored_seqno cs
|
||||
// 3 INT 35 LSHIFT# 3 INT RAWRESERVE // reserve all but 103 Grams from the balance
|
||||
8 LDU LDREF s0 s2 XCHG // public_key stored_seqno cs _45 mode
|
||||
SENDRAWMSG // public_key stored_seqno cs
|
||||
}>
|
||||
ENDS INC // public_key seqno'
|
||||
NEWC 32 STU 256 STU ENDC c4 POP
|
||||
)ABCD";
|
||||
return fift::compile_asm(code).move_as_ok();
|
||||
}
|
||||
|
||||
TEST(Tonlib, TestWallet) {
|
||||
LOG(ERROR) << td::base64_encode(std_boc_serialize(get_test_wallet_source()).move_as_ok());
|
||||
CHECK(get_test_wallet_source()->get_hash() == TestWallet::get_init_code()->get_hash());
|
||||
|
@ -115,37 +144,74 @@ TEST(Tonlib, TestWallet) {
|
|||
LOG(ERROR) << "-------";
|
||||
vm::load_cell_slice(vm::std_boc_deserialize(new_wallet_query).move_as_ok()).print_rec(std::cerr);
|
||||
CHECK(vm::std_boc_deserialize(new_wallet_query).move_as_ok()->get_hash() == res->get_hash());
|
||||
|
||||
fift_output.source_lookup.write_file("/main.fif", load_source("smartcont/wallet.fif")).ensure();
|
||||
auto dest = block::StdAddress::parse("Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX").move_as_ok();
|
||||
fift_output =
|
||||
fift::mem_run_fift(std::move(fift_output.source_lookup),
|
||||
{"aba", "new-wallet", "Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "123", "321"})
|
||||
.move_as_ok();
|
||||
auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data;
|
||||
auto gift_message = GenericAccount::create_ext_message(
|
||||
address, {}, TestWallet::make_a_gift_message(priv_key, 123, 321000000000ll, "TEST", dest));
|
||||
LOG(ERROR) << "-------";
|
||||
vm::load_cell_slice(gift_message).print_rec(std::cerr);
|
||||
LOG(ERROR) << "-------";
|
||||
vm::load_cell_slice(vm::std_boc_deserialize(wallet_query).move_as_ok()).print_rec(std::cerr);
|
||||
CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == gift_message->get_hash());
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> get_wallet_source() {
|
||||
td::Ref<vm::Cell> get_wallet_source_fc() {
|
||||
return fift::compile_asm(load_source("smartcont/wallet-code.fif"), "", false).move_as_ok();
|
||||
}
|
||||
|
||||
TEST(Tonlib, Wallet) {
|
||||
LOG(ERROR) << td::base64_encode(std_boc_serialize(get_wallet_source()).move_as_ok());
|
||||
CHECK(get_wallet_source()->get_hash() == Wallet::get_init_code()->get_hash());
|
||||
// TODO: fix ater new-wallet supports new type of wallet
|
||||
//auto fift_output = fift::mem_run_fift(load_source("smartcont/new-wallet.fif"), {"aba", "0"}).move_as_ok();
|
||||
|
||||
//auto new_wallet_pk = fift_output.source_lookup.read_file("new-wallet.pk").move_as_ok().data;
|
||||
//auto new_wallet_query = fift_output.source_lookup.read_file("new-wallet-query.boc").move_as_ok().data;
|
||||
//auto new_wallet_addr = fift_output.source_lookup.read_file("new-wallet.addr").move_as_ok().data;
|
||||
auto fift_output = fift::mem_run_fift(load_source("smartcont/new-wallet-v2.fif"), {"aba", "0"}).move_as_ok();
|
||||
|
||||
//td::Ed25519::PrivateKey priv_key{td::SecureString{new_wallet_pk}};
|
||||
//auto pub_key = priv_key.get_public_key().move_as_ok();
|
||||
//auto init_state = TestWallet::get_init_state(pub_key);
|
||||
//auto init_message = TestWallet::get_init_message(priv_key);
|
||||
//auto address = GenericAccount::get_address(0, init_state);
|
||||
auto new_wallet_pk = fift_output.source_lookup.read_file("new-wallet.pk").move_as_ok().data;
|
||||
auto new_wallet_query = fift_output.source_lookup.read_file("new-wallet-query.boc").move_as_ok().data;
|
||||
auto new_wallet_addr = fift_output.source_lookup.read_file("new-wallet.addr").move_as_ok().data;
|
||||
|
||||
//CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32));
|
||||
td::Ed25519::PrivateKey priv_key{td::SecureString{new_wallet_pk}};
|
||||
auto pub_key = priv_key.get_public_key().move_as_ok();
|
||||
auto init_state = Wallet::get_init_state(pub_key);
|
||||
auto init_message = Wallet::get_init_message(priv_key);
|
||||
auto address = GenericAccount::get_address(0, init_state);
|
||||
|
||||
//td::Ref<vm::Cell> res = GenericAccount::create_ext_message(address, init_state, init_message);
|
||||
CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32));
|
||||
|
||||
//LOG(ERROR) << "-------";
|
||||
//vm::load_cell_slice(res).print_rec(std::cerr);
|
||||
//LOG(ERROR) << "-------";
|
||||
//vm::load_cell_slice(vm::std_boc_deserialize(new_wallet_query).move_as_ok()).print_rec(std::cerr);
|
||||
//CHECK(vm::std_boc_deserialize(new_wallet_query).move_as_ok()->get_hash() == res->get_hash());
|
||||
td::Ref<vm::Cell> res = GenericAccount::create_ext_message(address, init_state, init_message);
|
||||
|
||||
LOG(ERROR) << "-------";
|
||||
vm::load_cell_slice(res).print_rec(std::cerr);
|
||||
LOG(ERROR) << "-------";
|
||||
vm::load_cell_slice(vm::std_boc_deserialize(new_wallet_query).move_as_ok()).print_rec(std::cerr);
|
||||
CHECK(vm::std_boc_deserialize(new_wallet_query).move_as_ok()->get_hash() == res->get_hash());
|
||||
|
||||
fift_output.source_lookup.write_file("/main.fif", load_source("smartcont/wallet-v2.fif")).ensure();
|
||||
class ZeroOsTime : public fift::OsTime {
|
||||
public:
|
||||
td::uint32 now() override {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
fift_output.source_lookup.set_os_time(std::make_unique<ZeroOsTime>());
|
||||
auto dest = block::StdAddress::parse("Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX").move_as_ok();
|
||||
fift_output =
|
||||
fift::mem_run_fift(std::move(fift_output.source_lookup),
|
||||
{"aba", "new-wallet", "Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "123", "321"})
|
||||
.move_as_ok();
|
||||
auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data;
|
||||
auto gift_message = GenericAccount::create_ext_message(
|
||||
address, {}, Wallet::make_a_gift_message(priv_key, 123, 60, 321000000000ll, "TESTv2", dest));
|
||||
LOG(ERROR) << "-------";
|
||||
vm::load_cell_slice(gift_message).print_rec(std::cerr);
|
||||
LOG(ERROR) << "-------";
|
||||
vm::load_cell_slice(vm::std_boc_deserialize(wallet_query).move_as_ok()).print_rec(std::cerr);
|
||||
CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == gift_message->get_hash());
|
||||
}
|
||||
|
||||
TEST(Tonlib, TestGiver) {
|
||||
|
@ -201,20 +267,21 @@ static auto sync_send = [](auto &client, auto query) {
|
|||
|
||||
TEST(Tonlib, InitClose) {
|
||||
using tonlib_api::make_object;
|
||||
auto cfg = [](auto str) { return make_object<tonlib_api::config>(str, "", false, false); };
|
||||
{
|
||||
Client client;
|
||||
sync_send(client, make_object<tonlib_api::close>()).ensure();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>("", ".", false))).ensure_error();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure_error();
|
||||
}
|
||||
{
|
||||
Client client;
|
||||
sync_send(client, make_object<tonlib_api::init>(nullptr)).ensure_error();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>("fdajkfldsjkafld", ".", false)))
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(cfg("fdajkfldsjkafld"), ".")))
|
||||
.ensure_error();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>("", "fdhskfds", false)))
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "fdhskfds")))
|
||||
.ensure_error();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>("", ".", false))).ensure();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>("", ".", false))).ensure_error();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure_error();
|
||||
|
||||
td::Slice bad_config = R"abc(
|
||||
{
|
||||
|
@ -223,11 +290,11 @@ TEST(Tonlib, InitClose) {
|
|||
}
|
||||
)abc";
|
||||
|
||||
sync_send(client, make_object<tonlib_api::options_setConfig>(bad_config.str())).ensure_error();
|
||||
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();
|
||||
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>("", ".", false))).ensure_error();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure_error();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -317,12 +384,32 @@ TEST(Tonlib, Keys) {
|
|||
CHECK(decrypted_key.private_key.as_octet_string() == other_decrypted_key.private_key.as_octet_string());
|
||||
}
|
||||
|
||||
TEST(Tonlib, ParseAddres) {
|
||||
using tonlib_api::make_object;
|
||||
Client client;
|
||||
|
||||
// init
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure();
|
||||
|
||||
sync_send(client, make_object<tonlib_api::unpackAccountAddress>("hello")).ensure_error();
|
||||
auto addr =
|
||||
sync_send(client,
|
||||
make_object<tonlib_api::unpackAccountAddress>("Ef9Tj6fMJP-OqhAdhKXxq36DL-HYSzCc3-9O6UNzqsgPfYFX"))
|
||||
.move_as_ok();
|
||||
ASSERT_EQ(-1, addr->workchain_id_);
|
||||
ASSERT_EQ(true, addr->bounceable_);
|
||||
ASSERT_EQ(false, addr->testnet_);
|
||||
|
||||
auto addr_str = sync_send(client, make_object<tonlib_api::packAccountAddress>(std::move(addr))).move_as_ok();
|
||||
ASSERT_EQ("Ef9Tj6fMJP-OqhAdhKXxq36DL-HYSzCc3-9O6UNzqsgPfYFX", addr_str->account_address_);
|
||||
}
|
||||
|
||||
TEST(Tonlib, KeysApi) {
|
||||
using tonlib_api::make_object;
|
||||
Client client;
|
||||
|
||||
// init
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>("", ".", false))).ensure();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure();
|
||||
auto local_password = td::SecureString("local password");
|
||||
auto mnemonic_password = td::SecureString("mnemonic password");
|
||||
{
|
||||
|
|
|
@ -118,7 +118,7 @@ void transfer_grams(Client& client, std::string from, std::string to, td::int64
|
|||
auto balance = get_balance(client, to);
|
||||
sync_send(client, tonlib_api::make_object<tonlib_api::generic_sendGrams>(
|
||||
std::move(input_key), tonlib_api::make_object<tonlib_api::accountAddress>(from),
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(to), amount, "GIFT"))
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(to), amount, 0, true, "GIFT"))
|
||||
.ensure();
|
||||
while (balance == get_balance(client, to)) {
|
||||
client.receive(1);
|
||||
|
@ -196,7 +196,8 @@ int main(int argc, char* argv[]) {
|
|||
|
||||
Client client;
|
||||
{
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(global_config_str, ".", false)))
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(
|
||||
make_object<tonlib_api::config>(global_config_str, "", false, false), ".")))
|
||||
.ensure();
|
||||
}
|
||||
//dump_transaction_history(client, get_test_giver_address(client));
|
||||
|
@ -209,7 +210,8 @@ int main(int argc, char* argv[]) {
|
|||
return 0;
|
||||
{
|
||||
// init
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(global_config_str, ".", false)))
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(
|
||||
make_object<tonlib_api::config>(global_config_str, "", false, false), ".")))
|
||||
.ensure();
|
||||
|
||||
auto key = sync_send(client, make_object<tonlib_api::createNewKey>(
|
||||
|
@ -224,7 +226,9 @@ int main(int argc, char* argv[]) {
|
|||
auto public_key_raw = key->public_key_;
|
||||
td::Ed25519::PublicKey public_key_std(td::SecureString{public_key_raw});
|
||||
|
||||
sync_send(client, make_object<tonlib_api::options_setConfig>(global_config_str)).ensure();
|
||||
sync_send(client, make_object<tonlib_api::options_setConfig>(
|
||||
make_object<tonlib_api::config>(global_config_str, "", false, false)))
|
||||
.ensure();
|
||||
|
||||
auto wallet_addr = GenericAccount::get_address(0, TestWallet::get_init_state(public_key_std));
|
||||
{
|
||||
|
@ -307,10 +311,10 @@ int main(int argc, char* argv[]) {
|
|||
}
|
||||
|
||||
{
|
||||
sync_send(client,
|
||||
make_object<tonlib_api::generic_sendGrams>(
|
||||
create_input_key(), make_object<tonlib_api::accountAddress>(wallet_addr.rserialize()),
|
||||
make_object<tonlib_api::accountAddress>(test_giver_address), 1000000000ll * 3333 / 1000, "GIFT"))
|
||||
sync_send(client, make_object<tonlib_api::generic_sendGrams>(
|
||||
create_input_key(), make_object<tonlib_api::accountAddress>(wallet_addr.rserialize()),
|
||||
make_object<tonlib_api::accountAddress>(test_giver_address), 1000000000ll * 3333 / 1000, 0,
|
||||
true, "GIFT"))
|
||||
.ensure();
|
||||
}
|
||||
while (true) {
|
||||
|
|
|
@ -21,6 +21,45 @@
|
|||
#include "td/utils/JsonBuilder.h"
|
||||
|
||||
namespace tonlib {
|
||||
td::Result<ton::BlockIdExt> parse_block_id_ext(td::JsonObject &obj) {
|
||||
ton::WorkchainId zero_workchain_id;
|
||||
{
|
||||
TRY_RESULT(wc, td::get_json_object_int_field(obj, "workchain"));
|
||||
zero_workchain_id = wc;
|
||||
}
|
||||
ton::ShardId zero_shard_id; // uint64
|
||||
{
|
||||
TRY_RESULT(shard_id, td::get_json_object_long_field(obj, "shard"));
|
||||
zero_shard_id = static_cast<ton::ShardId>(shard_id);
|
||||
}
|
||||
ton::BlockSeqno zero_seqno;
|
||||
{
|
||||
TRY_RESULT(seqno, td::get_json_object_int_field(obj, "seqno"));
|
||||
zero_seqno = seqno;
|
||||
}
|
||||
|
||||
ton::RootHash zero_root_hash;
|
||||
{
|
||||
TRY_RESULT(hash_b64, td::get_json_object_string_field(obj, "root_hash"));
|
||||
TRY_RESULT(hash, td::base64_decode(hash_b64));
|
||||
if (hash.size() * 8 != ton::RootHash::size()) {
|
||||
return td::Status::Error("Invalid config (8)");
|
||||
}
|
||||
zero_root_hash = ton::RootHash(td::ConstBitPtr(td::Slice(hash).ubegin()));
|
||||
}
|
||||
ton::FileHash zero_file_hash;
|
||||
{
|
||||
TRY_RESULT(hash_b64, td::get_json_object_string_field(obj, "file_hash"));
|
||||
TRY_RESULT(hash, td::base64_decode(hash_b64));
|
||||
if (hash.size() * 8 != ton::FileHash::size()) {
|
||||
return td::Status::Error("Invalid config (9)");
|
||||
}
|
||||
zero_file_hash = ton::RootHash(td::ConstBitPtr(td::Slice(hash).ubegin()));
|
||||
}
|
||||
|
||||
return ton::BlockIdExt(zero_workchain_id, zero_shard_id, zero_seqno, std::move(zero_root_hash),
|
||||
std::move(zero_file_hash));
|
||||
}
|
||||
td::Result<Config> Config::parse(std::string str) {
|
||||
TRY_RESULT(json, td::json_decode(str));
|
||||
if (json.type() != td::JsonValue::Type::Object) {
|
||||
|
@ -74,45 +113,13 @@ td::Result<Config> Config::parse(std::string str) {
|
|||
return td::Status::Error("Invalid config (7)");
|
||||
}
|
||||
TRY_RESULT(zero_state_obj, td::get_json_object_field(validator, "zero_state", td::JsonValue::Type::Object, false));
|
||||
auto &zero_state = zero_state_obj.get_object();
|
||||
|
||||
ton::WorkchainId zero_workchain_id;
|
||||
{
|
||||
TRY_RESULT(wc, td::get_json_object_int_field(zero_state, "workchain"));
|
||||
zero_workchain_id = wc;
|
||||
TRY_RESULT(zero_state_id, parse_block_id_ext(zero_state_obj.get_object()));
|
||||
res.zero_state_id = zero_state_id;
|
||||
auto r_init_block_obj = td::get_json_object_field(validator, "init_block", td::JsonValue::Type::Object, false);
|
||||
if (r_init_block_obj.is_ok()) {
|
||||
TRY_RESULT(init_block_id, parse_block_id_ext(r_init_block_obj.move_as_ok().get_object()));
|
||||
res.init_block_id = init_block_id;
|
||||
}
|
||||
ton::ShardId zero_shard_id; // uint64
|
||||
{
|
||||
TRY_RESULT(shard_id, td::get_json_object_long_field(zero_state, "shard"));
|
||||
zero_shard_id = static_cast<ton::ShardId>(shard_id);
|
||||
}
|
||||
ton::BlockSeqno zero_seqno;
|
||||
{
|
||||
TRY_RESULT(seqno, td::get_json_object_int_field(zero_state, "seqno"));
|
||||
zero_seqno = seqno;
|
||||
}
|
||||
|
||||
ton::RootHash zero_root_hash;
|
||||
{
|
||||
TRY_RESULT(hash_b64, td::get_json_object_string_field(zero_state, "root_hash"));
|
||||
TRY_RESULT(hash, td::base64_decode(hash_b64));
|
||||
if (hash.size() * 8 != ton::RootHash::size()) {
|
||||
return td::Status::Error("Invalid config (8)");
|
||||
}
|
||||
zero_root_hash = ton::RootHash(td::ConstBitPtr(td::Slice(hash).ubegin()));
|
||||
}
|
||||
ton::FileHash zero_file_hash;
|
||||
{
|
||||
TRY_RESULT(hash_b64, td::get_json_object_string_field(zero_state, "file_hash"));
|
||||
TRY_RESULT(hash, td::base64_decode(hash_b64));
|
||||
if (hash.size() * 8 != ton::FileHash::size()) {
|
||||
return td::Status::Error("Invalid config (9)");
|
||||
}
|
||||
zero_file_hash = ton::RootHash(td::ConstBitPtr(td::Slice(hash).ubegin()));
|
||||
}
|
||||
|
||||
res.zero_state_id = ton::BlockIdExt(zero_workchain_id, zero_shard_id, zero_seqno, std::move(zero_root_hash),
|
||||
std::move(zero_file_hash));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ struct Config {
|
|||
td::IPAddress address;
|
||||
};
|
||||
ton::BlockIdExt zero_state_id;
|
||||
ton::BlockIdExt init_block_id;
|
||||
std::vector<LiteClient> lite_clients;
|
||||
static td::Result<Config> parse(std::string str);
|
||||
};
|
||||
|
|
|
@ -29,27 +29,70 @@ td::StringBuilder& operator<<(td::StringBuilder& sb, const LastBlockState& state
|
|||
<< td::tag("last_key_block", state.last_key_block_id.to_str()) << td::tag("utime", state.utime);
|
||||
}
|
||||
|
||||
LastBlock::LastBlock(ExtClientRef client, LastBlockState state, td::unique_ptr<Callback> callback)
|
||||
: state_(std::move(state)), callback_(std::move(callback)) {
|
||||
LastBlock::LastBlock(ExtClientRef client, LastBlockState state, Config config, td::unique_ptr<Callback> callback)
|
||||
: state_(std::move(state)), config_(std::move(config)), callback_(std::move(callback)) {
|
||||
client_.set_client(client);
|
||||
if (!config_.init_block_id.is_valid()) {
|
||||
check_init_block_state_ = QueryState::Done;
|
||||
}
|
||||
}
|
||||
|
||||
void LastBlock::get_last_block(td::Promise<LastBlockState> promise) {
|
||||
if (has_fatal_error()) {
|
||||
promise.set_error(fatal_error_.clone());
|
||||
return;
|
||||
}
|
||||
if (promises_.empty() && get_last_block_state_ == QueryState::Done) {
|
||||
get_last_block_state_ = QueryState::Empty;
|
||||
}
|
||||
promises_.push_back(std::move(promise));
|
||||
sync_loop();
|
||||
}
|
||||
|
||||
void LastBlock::sync_loop() {
|
||||
if (promises_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
update_zero_state(state_.zero_state_id);
|
||||
update_zero_state(ton::ZeroStateIdExt(config_.zero_state_id.id.workchain, config_.zero_state_id.root_hash,
|
||||
config_.zero_state_id.file_hash));
|
||||
|
||||
if (get_mc_info_state_ == QueryState::Empty) {
|
||||
get_mc_info_state_ = QueryState::Active;
|
||||
client_.send_query(ton::lite_api::liteServer_getMasterchainInfo(),
|
||||
[this](auto r_info) { this->on_masterchain_info(std::move(r_info)); });
|
||||
}
|
||||
|
||||
if (get_last_block_state_ == QueryState::Empty) {
|
||||
get_last_block_state_ = QueryState::Active;
|
||||
total_sync_ = td::Timer();
|
||||
validate_ = td::Timer(true);
|
||||
queries_ = 0;
|
||||
LOG(INFO) << "Begin last block synchronization " << state_;
|
||||
do_get_last_block();
|
||||
}
|
||||
promises_.push_back(std::move(promise));
|
||||
|
||||
if (check_init_block_state_ == QueryState::Empty) {
|
||||
if (state_.last_block_id.id.seqno >= config_.init_block_id.id.seqno) {
|
||||
check_init_block_state_ = QueryState::Active;
|
||||
// validate
|
||||
//total_sync_ = td::Timer();
|
||||
//validate_ = td::Timer(true);
|
||||
//queries_ = 0;
|
||||
LOG(INFO) << "Begin last block synchronization (check init_block)" << state_;
|
||||
do_check_init_block(state_.last_key_block_id);
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
if (get_mc_info_state_ == QueryState::Done && get_last_block_state_ == QueryState::Done &&
|
||||
check_init_block_state_ == QueryState::Done) {
|
||||
on_sync_ok();
|
||||
}
|
||||
}
|
||||
|
||||
void LastBlock::do_get_last_block() {
|
||||
//client_.send_query(ton::lite_api::liteServer_getMasterchainInfo(),
|
||||
//[this](auto r_info) { this->on_masterchain_info(std::move(r_info)); });
|
||||
//return;
|
||||
|
||||
//liteServer.getBlockProof mode:# known_block:tonNode.blockIdExt target_block:mode.0?tonNode.blockIdExt = liteServer.PartialBlockProof;
|
||||
queries_++;
|
||||
client_.send_query(
|
||||
|
@ -59,14 +102,19 @@ void LastBlock::do_get_last_block() {
|
|||
});
|
||||
}
|
||||
|
||||
td::Result<bool> LastBlock::process_block_proof(
|
||||
void LastBlock::do_check_init_block(ton::BlockIdExt from) {
|
||||
//liteServer.getBlockProof mode:# known_block:tonNode.blockIdExt target_block:mode.0?tonNode.blockIdExt = liteServer.PartialBlockProof;
|
||||
//queries_++;
|
||||
client_.send_query(ton::lite_api::liteServer_getBlockProof(1, create_tl_lite_block_id(from),
|
||||
create_tl_lite_block_id(config_.init_block_id)),
|
||||
[this, from = state_.last_key_block_id](auto r_block_proof) {
|
||||
this->on_init_block_proof(from, std::move(r_block_proof));
|
||||
});
|
||||
}
|
||||
|
||||
td::Result<std::unique_ptr<block::BlockProofChain>> LastBlock::process_block_proof(
|
||||
ton::BlockIdExt from,
|
||||
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof) {
|
||||
validate_.resume();
|
||||
SCOPE_EXIT {
|
||||
validate_.pause();
|
||||
};
|
||||
|
||||
TRY_RESULT(block_proof, std::move(r_block_proof));
|
||||
LOG(DEBUG) << "Got proof FROM\n" << to_string(block_proof->from_) << "TO\n" << to_string(block_proof->to_);
|
||||
TRY_RESULT(chain, liteclient::deserialize_proof_chain(std::move(block_proof)));
|
||||
|
@ -86,51 +134,82 @@ td::Result<bool> LastBlock::process_block_proof(
|
|||
if (is_changed) {
|
||||
callback_->on_state_changed(state_);
|
||||
}
|
||||
return chain->complete;
|
||||
return std::move(chain);
|
||||
}
|
||||
|
||||
void LastBlock::on_block_proof(
|
||||
ton::BlockIdExt from,
|
||||
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof) {
|
||||
auto r_is_ready = process_block_proof(from, std::move(r_block_proof));
|
||||
validate_.resume();
|
||||
auto r_chain = process_block_proof(from, std::move(r_block_proof));
|
||||
validate_.pause();
|
||||
bool is_ready;
|
||||
if (r_is_ready.is_error()) {
|
||||
LOG(WARNING) << "Error during last block synchronization " << r_is_ready.error();
|
||||
if (r_chain.is_error()) {
|
||||
LOG(WARNING) << "Error during last block synchronization " << r_chain.error();
|
||||
if (config_.init_block_id.is_valid()) {
|
||||
if (state_.last_key_block_id.id.seqno < config_.init_block_id.id.seqno) {
|
||||
on_sync_error(td::Status::Error(PSLICE() << "Sync failed and we can't validate config.init_block: "
|
||||
<< r_chain.move_as_error()));
|
||||
}
|
||||
}
|
||||
is_ready = true;
|
||||
} else {
|
||||
is_ready = r_is_ready.move_as_ok();
|
||||
is_ready = r_chain.ok()->complete;
|
||||
}
|
||||
if (is_ready) {
|
||||
LOG(INFO) << "End last block synchronization " << state_ << "\n"
|
||||
<< " net queries: " << queries_ << "\n"
|
||||
<< " total: " << total_sync_ << " validation: " << validate_;
|
||||
for (auto& promise : promises_) {
|
||||
auto state = state_;
|
||||
promise.set_value(std::move(state));
|
||||
}
|
||||
promises_.clear();
|
||||
get_last_block_state_ = QueryState::Done;
|
||||
sync_loop();
|
||||
} else {
|
||||
do_get_last_block();
|
||||
}
|
||||
}
|
||||
|
||||
void LastBlock::on_init_block_proof(
|
||||
ton::BlockIdExt from,
|
||||
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof) {
|
||||
validate_.resume();
|
||||
auto r_chain = process_block_proof(from, std::move(r_block_proof));
|
||||
validate_.pause();
|
||||
if (r_chain.is_error()) {
|
||||
check_init_block_state_ = QueryState::Empty;
|
||||
on_sync_error(
|
||||
td::Status::Error(PSLICE() << "Error during last block synchronization (check init_block)" << r_chain.error()));
|
||||
return;
|
||||
}
|
||||
auto chain = r_chain.move_as_ok();
|
||||
if (chain->complete) {
|
||||
LOG(INFO) << "End last block synchronization " << state_ << "\n"
|
||||
<< " net queries: " << queries_ << "\n"
|
||||
<< " total: " << total_sync_ << " validation: " << validate_;
|
||||
get_last_block_state_ = QueryState::Done;
|
||||
sync_loop();
|
||||
} else {
|
||||
do_check_init_block(chain->to);
|
||||
}
|
||||
}
|
||||
|
||||
void LastBlock::on_masterchain_info(
|
||||
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_masterchainInfo>> r_info) {
|
||||
if (r_info.is_ok()) {
|
||||
auto info = r_info.move_as_ok();
|
||||
update_zero_state(create_zero_state_id(info->init_));
|
||||
update_mc_last_block(create_block_id(info->last_));
|
||||
get_mc_info_state_ = QueryState::Done;
|
||||
} else {
|
||||
get_mc_info_state_ = QueryState::Empty;
|
||||
LOG(WARNING) << "Failed liteServer_getMasterchainInfo " << r_info.error();
|
||||
on_sync_error(r_info.move_as_error());
|
||||
}
|
||||
for (auto& promise : promises_) {
|
||||
auto state = state_;
|
||||
promise.set_value(std::move(state));
|
||||
}
|
||||
promises_.clear();
|
||||
sync_loop();
|
||||
}
|
||||
|
||||
void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id) {
|
||||
if (has_fatal_error()) {
|
||||
return;
|
||||
}
|
||||
if (!zero_state_id.is_valid()) {
|
||||
LOG(ERROR) << "Ignore invalid zero state update";
|
||||
return;
|
||||
|
@ -142,17 +221,18 @@ void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (state_.zero_state_id == state_.zero_state_id) {
|
||||
if (state_.zero_state_id == zero_state_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(FATAL) << "Masterchain zerostate mismatch: expected: " << state_.zero_state_id.to_str() << ", found "
|
||||
<< zero_state_id.to_str();
|
||||
// TODO: all other updates will be inconsitent.
|
||||
// One will have to restart ton client
|
||||
on_fatal_error(td::Status::Error(PSLICE() << "Masterchain zerostate mismatch: expected: "
|
||||
<< state_.zero_state_id.to_str() << ", found " << zero_state_id.to_str()));
|
||||
}
|
||||
|
||||
bool LastBlock::update_mc_last_block(ton::BlockIdExt mc_block_id) {
|
||||
if (has_fatal_error()) {
|
||||
return false;
|
||||
}
|
||||
if (!mc_block_id.is_valid()) {
|
||||
LOG(ERROR) << "Ignore invalid masterchain block";
|
||||
return false;
|
||||
|
@ -166,6 +246,9 @@ bool LastBlock::update_mc_last_block(ton::BlockIdExt mc_block_id) {
|
|||
}
|
||||
|
||||
bool LastBlock::update_mc_last_key_block(ton::BlockIdExt mc_key_block_id) {
|
||||
if (has_fatal_error()) {
|
||||
return false;
|
||||
}
|
||||
if (!mc_key_block_id.is_valid()) {
|
||||
LOG(ERROR) << "Ignore invalid masterchain block";
|
||||
return false;
|
||||
|
@ -183,4 +266,26 @@ void LastBlock::update_utime(td::int64 utime) {
|
|||
state_.utime = utime;
|
||||
}
|
||||
}
|
||||
|
||||
void LastBlock::on_sync_ok() {
|
||||
for (auto& promise : promises_) {
|
||||
auto state = state_;
|
||||
promise.set_value(std::move(state));
|
||||
}
|
||||
promises_.clear();
|
||||
}
|
||||
void LastBlock::on_sync_error(td::Status status) {
|
||||
for (auto& promise : promises_) {
|
||||
promise.set_error(status.clone());
|
||||
}
|
||||
promises_.clear();
|
||||
}
|
||||
void LastBlock::on_fatal_error(td::Status status) {
|
||||
fatal_error_ = std::move(status);
|
||||
on_sync_error(fatal_error_.clone());
|
||||
}
|
||||
|
||||
bool LastBlock::has_fatal_error() const {
|
||||
return fatal_error_.is_error();
|
||||
}
|
||||
} // namespace tonlib
|
||||
|
|
|
@ -19,8 +19,12 @@
|
|||
#pragma once
|
||||
#include "td/actor/actor.h"
|
||||
|
||||
#include "tonlib/Config.h"
|
||||
#include "tonlib/ExtClient.h"
|
||||
|
||||
namespace block {
|
||||
struct BlockProofChain;
|
||||
}
|
||||
namespace tonlib {
|
||||
td::StringBuilder &operator<<(td::StringBuilder &sb, const LastBlockState &state);
|
||||
template <unsigned int N, class StorerT>
|
||||
|
@ -116,14 +120,22 @@ class LastBlock : public td::actor::Actor {
|
|||
virtual void on_state_changed(LastBlockState state) = 0;
|
||||
};
|
||||
|
||||
explicit LastBlock(ExtClientRef client, LastBlockState state, td::unique_ptr<Callback> callback);
|
||||
explicit LastBlock(ExtClientRef client, LastBlockState state, Config config, td::unique_ptr<Callback> callback);
|
||||
void get_last_block(td::Promise<LastBlockState> promise);
|
||||
|
||||
private:
|
||||
ExtClient client_;
|
||||
LastBlockState state_;
|
||||
Config config_;
|
||||
td::unique_ptr<Callback> callback_;
|
||||
|
||||
td::Status fatal_error_;
|
||||
|
||||
enum class QueryState { Empty, Active, Done };
|
||||
QueryState get_mc_info_state_{QueryState::Empty};
|
||||
QueryState get_last_block_state_{QueryState::Empty};
|
||||
QueryState check_init_block_state_{QueryState::Empty};
|
||||
|
||||
// stats
|
||||
td::Timer total_sync_;
|
||||
td::Timer validate_;
|
||||
|
@ -131,11 +143,15 @@ class LastBlock : public td::actor::Actor {
|
|||
|
||||
std::vector<td::Promise<LastBlockState>> promises_;
|
||||
|
||||
void do_get_last_block();
|
||||
void do_check_init_block(ton::BlockIdExt from);
|
||||
void on_init_block_proof(
|
||||
ton::BlockIdExt from,
|
||||
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof);
|
||||
void on_masterchain_info(td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_masterchainInfo>> r_info);
|
||||
void do_get_last_block();
|
||||
void on_block_proof(ton::BlockIdExt from,
|
||||
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof);
|
||||
td::Result<bool> process_block_proof(
|
||||
td::Result<std::unique_ptr<block::BlockProofChain>> process_block_proof(
|
||||
ton::BlockIdExt from,
|
||||
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof);
|
||||
|
||||
|
@ -144,5 +160,12 @@ class LastBlock : public td::actor::Actor {
|
|||
bool update_mc_last_block(ton::BlockIdExt mc_block_id);
|
||||
bool update_mc_last_key_block(ton::BlockIdExt mc_key_block_id);
|
||||
void update_utime(td::int64 utime);
|
||||
|
||||
void on_sync_ok();
|
||||
void on_sync_error(td::Status status);
|
||||
void on_fatal_error(td::Status status);
|
||||
bool has_fatal_error() const;
|
||||
|
||||
void sync_loop();
|
||||
};
|
||||
} // namespace tonlib
|
||||
|
|
|
@ -44,13 +44,16 @@ td::Ref<vm::Cell> TestWallet::make_a_gift_message(const td::Ed25519::PrivateKey&
|
|||
td::BigInt256 dest_addr;
|
||||
dest_addr.import_bits(dest_address.addr.as_bitslice());
|
||||
vm::CellBuilder cb;
|
||||
cb.append_cellslice(binary_bitstring_to_cellslice("b{010000100}").move_as_ok())
|
||||
cb.append_cellslice(binary_bitstring_to_cellslice("b{01}").move_as_ok())
|
||||
.store_long(dest_address.bounceable, 1)
|
||||
.append_cellslice(binary_bitstring_to_cellslice("b{000100}").move_as_ok())
|
||||
.store_long(dest_address.workchain, 8)
|
||||
.store_int256(dest_addr, 256);
|
||||
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms));
|
||||
auto message_inner = cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4).store_bytes(message).finalize();
|
||||
auto message_outer = vm::CellBuilder().store_long(seqno, 32).store_long(1, 8).store_ref(message_inner).finalize();
|
||||
std::string seq_no(4, 0);
|
||||
td::int8 send_mode = 3;
|
||||
auto message_outer =
|
||||
vm::CellBuilder().store_long(seqno, 32).store_long(send_mode, 8).store_ref(message_inner).finalize();
|
||||
auto signature = private_key.sign(message_outer->get_hash().as_slice()).move_as_ok();
|
||||
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();
|
||||
}
|
||||
|
|
|
@ -74,7 +74,6 @@ struct RawAccountState {
|
|||
td::Ref<vm::CellSlice> code;
|
||||
td::Ref<vm::CellSlice> data;
|
||||
block::AccountState::Info info;
|
||||
td::int64 sync_utime = 0;
|
||||
};
|
||||
|
||||
td::Result<td::int64> to_balance_or_throw(td::Ref<vm::CellSlice> balance_ref) {
|
||||
|
@ -191,7 +190,7 @@ class GetRawAccountState : public td::actor::Actor {
|
|||
auto serialized_state = account_state.state.clone();
|
||||
RawAccountState res;
|
||||
res.info = std::move(info);
|
||||
res.sync_utime = last_block_.utime;
|
||||
LOG_IF(ERROR, res.info.gen_utime > last_block_.utime) << res.info.gen_utime << " " << last_block_.utime;
|
||||
auto cell = res.info.root;
|
||||
if (cell.is_null()) {
|
||||
return res;
|
||||
|
@ -307,44 +306,54 @@ void TonlibClient::init_ext_client() {
|
|||
private:
|
||||
td::actor::ActorShared<> parent_;
|
||||
};
|
||||
ext_client_outbound_ = {};
|
||||
ref_cnt_++;
|
||||
raw_client_ = ExtClientLazy::create(lite_client.adnl_id, lite_client.address,
|
||||
td::make_unique<Callback>(td::actor::actor_shared()));
|
||||
}
|
||||
}
|
||||
|
||||
void TonlibClient::update_last_block_state(LastBlockState state) {
|
||||
last_block_storage_.save_state("none", state);
|
||||
void TonlibClient::update_last_block_state(LastBlockState state, td::uint32 config_generation) {
|
||||
if (config_generation == config_generation_) {
|
||||
last_block_storage_.save_state(blockchain_name_, state);
|
||||
}
|
||||
}
|
||||
|
||||
void TonlibClient::init_last_block() {
|
||||
ref_cnt_++;
|
||||
class Callback : public LastBlock::Callback {
|
||||
public:
|
||||
Callback(td::actor::ActorShared<TonlibClient> client) : client_(std::move(client)) {
|
||||
Callback(td::actor::ActorShared<TonlibClient> client, td::uint32 config_generation)
|
||||
: client_(std::move(client)), config_generation_(config_generation) {
|
||||
}
|
||||
void on_state_changed(LastBlockState state) override {
|
||||
send_closure(client_, &TonlibClient::update_last_block_state, std::move(state));
|
||||
send_closure(client_, &TonlibClient::update_last_block_state, std::move(state), config_generation_);
|
||||
}
|
||||
|
||||
private:
|
||||
td::actor::ActorShared<TonlibClient> client_;
|
||||
td::uint32 config_generation_;
|
||||
};
|
||||
LastBlockState state;
|
||||
|
||||
auto r_state = last_block_storage_.get_state("none");
|
||||
if (r_state.is_error()) {
|
||||
LOG(WARNING) << "Unknown LastBlockState: " << r_state.error();
|
||||
td::Result<LastBlockState> r_state;
|
||||
if (!ignore_cache_) {
|
||||
r_state = last_block_storage_.get_state(blockchain_name_);
|
||||
}
|
||||
if (ignore_cache_ || r_state.is_error()) {
|
||||
LOG_IF(WARNING, !ignore_cache_) << "Unknown LastBlockState: " << r_state.error();
|
||||
state.zero_state_id = ton::ZeroStateIdExt(config_.zero_state_id.id.workchain, config_.zero_state_id.root_hash,
|
||||
config_.zero_state_id.file_hash),
|
||||
state.last_block_id = config_.zero_state_id;
|
||||
state.last_key_block_id = config_.zero_state_id;
|
||||
last_block_storage_.save_state(blockchain_name_, state);
|
||||
} else {
|
||||
state = r_state.move_as_ok();
|
||||
}
|
||||
|
||||
raw_last_block_ = td::actor::create_actor<LastBlock>("LastBlock", get_client_ref(), std::move(state),
|
||||
td::make_unique<Callback>(td::actor::actor_shared(this)));
|
||||
raw_last_block_ =
|
||||
td::actor::create_actor<LastBlock>("LastBlock", get_client_ref(), std::move(state), config_,
|
||||
td::make_unique<Callback>(td::actor::actor_shared(this), config_generation_));
|
||||
client_.set_client(get_client_ref());
|
||||
}
|
||||
|
||||
|
@ -416,6 +425,8 @@ bool TonlibClient::is_static_request(td::int32 id) {
|
|||
case tonlib_api::testWallet_getAccountAddress::ID:
|
||||
case tonlib_api::wallet_getAccountAddress::ID:
|
||||
case tonlib_api::testGiver_getAccountAddress::ID:
|
||||
case tonlib_api::packAccountAddress::ID:
|
||||
case tonlib_api::unpackAccountAddress::ID:
|
||||
case tonlib_api::getBip39Hints::ID:
|
||||
case tonlib_api::setLogStream::ID:
|
||||
case tonlib_api::getLogStream::ID:
|
||||
|
@ -472,7 +483,7 @@ tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
|
|||
if (r_account_address.is_error()) {
|
||||
return status_to_tonlib_api(r_account_address.error());
|
||||
}
|
||||
return tonlib_api::make_object<tonlib_api::accountAddress>(r_account_address.ok().rserialize());
|
||||
return tonlib_api::make_object<tonlib_api::accountAddress>(r_account_address.ok().rserialize(true));
|
||||
}
|
||||
tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
|
||||
const tonlib_api::testWallet_getAccountAddress& request) {
|
||||
|
@ -480,7 +491,7 @@ tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
|
|||
if (r_account_address.is_error()) {
|
||||
return status_to_tonlib_api(r_account_address.error());
|
||||
}
|
||||
return tonlib_api::make_object<tonlib_api::accountAddress>(r_account_address.ok().rserialize());
|
||||
return tonlib_api::make_object<tonlib_api::accountAddress>(r_account_address.ok().rserialize(true));
|
||||
}
|
||||
tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
|
||||
const tonlib_api::wallet_getAccountAddress& request) {
|
||||
|
@ -488,11 +499,39 @@ tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
|
|||
if (r_account_address.is_error()) {
|
||||
return status_to_tonlib_api(r_account_address.error());
|
||||
}
|
||||
return tonlib_api::make_object<tonlib_api::accountAddress>(r_account_address.ok().rserialize());
|
||||
return tonlib_api::make_object<tonlib_api::accountAddress>(r_account_address.ok().rserialize(true));
|
||||
}
|
||||
tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
|
||||
const tonlib_api::testGiver_getAccountAddress& request) {
|
||||
return tonlib_api::make_object<tonlib_api::accountAddress>(TestGiver::address().rserialize());
|
||||
return tonlib_api::make_object<tonlib_api::accountAddress>(TestGiver::address().rserialize(true));
|
||||
}
|
||||
|
||||
tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
|
||||
const tonlib_api::unpackAccountAddress& request) {
|
||||
auto r_account_address = block::StdAddress::parse(request.account_address_);
|
||||
if (r_account_address.is_error()) {
|
||||
return status_to_tonlib_api(r_account_address.move_as_error());
|
||||
}
|
||||
auto account_address = r_account_address.move_as_ok();
|
||||
return tonlib_api::make_object<tonlib_api::unpackedAccountAddress>(
|
||||
account_address.workchain, account_address.bounceable, account_address.testnet,
|
||||
account_address.addr.as_slice().str());
|
||||
}
|
||||
|
||||
tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
|
||||
const tonlib_api::packAccountAddress& request) {
|
||||
if (!request.account_address_) {
|
||||
return status_to_tonlib_api(td::Status::Error(400, "Field account_address must not be empty"));
|
||||
}
|
||||
if (request.account_address_->addr_.size() != 32) {
|
||||
return status_to_tonlib_api(td::Status::Error(400, "Field account_address.addr must not be exactly 32 bytes"));
|
||||
}
|
||||
block::StdAddress addr;
|
||||
addr.workchain = request.account_address_->workchain_id_;
|
||||
addr.bounceable = request.account_address_->bounceable_;
|
||||
addr.testnet = request.account_address_->testnet_;
|
||||
addr.addr.as_slice().copy_from(request.account_address_->addr_);
|
||||
return tonlib_api::make_object<tonlib_api::accountAddress>(addr.rserialize(true));
|
||||
}
|
||||
|
||||
tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(tonlib_api::getBip39Hints& request) {
|
||||
|
@ -510,8 +549,7 @@ td::Status TonlibClient::do_request(const tonlib_api::init& request,
|
|||
}
|
||||
TRY_STATUS(key_storage_.set_directory(request.options_->keystore_directory_));
|
||||
TRY_STATUS(last_block_storage_.set_directory(request.options_->keystore_directory_));
|
||||
use_callbacks_for_network_ = request.options_->use_callbacks_for_network_;
|
||||
if (!request.options_->config_.empty()) {
|
||||
if (request.options_->config_) {
|
||||
TRY_STATUS(set_config(std::move(request.options_->config_)));
|
||||
}
|
||||
state_ = State::Running;
|
||||
|
@ -519,15 +557,26 @@ td::Status TonlibClient::do_request(const tonlib_api::init& request,
|
|||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status TonlibClient::set_config(std::string config) {
|
||||
if (config.empty()) {
|
||||
return td::Status::Error("config is empty");
|
||||
td::Status TonlibClient::set_config(object_ptr<tonlib_api::config> config) {
|
||||
if (!config) {
|
||||
return td::Status::Error(400, "config is empty");
|
||||
}
|
||||
TRY_RESULT(new_config, Config::parse(std::move(config)));
|
||||
if (new_config.lite_clients.empty()) {
|
||||
if (config->config_.empty()) {
|
||||
return td::Status::Error(400, "config is empty");
|
||||
}
|
||||
TRY_RESULT(new_config, Config::parse(std::move(config->config_)));
|
||||
if (new_config.lite_clients.empty() && !config->use_callbacks_for_network_) {
|
||||
return td::Status::Error("No lite clients in config");
|
||||
}
|
||||
config_ = std::move(new_config);
|
||||
config_generation_++;
|
||||
if (config->blockchain_name_.empty()) {
|
||||
blockchain_name_ = td::sha256(config_.zero_state_id.to_str()).substr(0, 16);
|
||||
} else {
|
||||
blockchain_name_ = config->blockchain_name_;
|
||||
}
|
||||
use_callbacks_for_network_ = config->use_callbacks_for_network_;
|
||||
ignore_cache_ = config->ignore_cache_;
|
||||
init_ext_client();
|
||||
init_last_block();
|
||||
return td::Status::OK();
|
||||
|
@ -541,9 +590,9 @@ td::Status TonlibClient::do_request(const tonlib_api::close& request,
|
|||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status TonlibClient::do_request(const tonlib_api::options_setConfig& request,
|
||||
td::Status TonlibClient::do_request(tonlib_api::options_setConfig& request,
|
||||
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
|
||||
TRY_STATUS(set_config(request.config_));
|
||||
TRY_STATUS(set_config(std::move(request.config_)));
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::ok>());
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
@ -572,8 +621,8 @@ td::Result<tonlib_api::object_ptr<tonlib_api::raw_accountState>> to_raw_accountS
|
|||
.as_slice()
|
||||
.str();
|
||||
}
|
||||
return tonlib_api::make_object<tonlib_api::raw_accountState>(raw_state.balance, std::move(code), std::move(data),
|
||||
to_transaction_id(raw_state.info), raw_state.sync_utime);
|
||||
return tonlib_api::make_object<tonlib_api::raw_accountState>(
|
||||
raw_state.balance, std::move(code), std::move(data), to_transaction_id(raw_state.info), raw_state.info.gen_utime);
|
||||
}
|
||||
|
||||
td::Result<std::string> to_std_address_or_throw(td::Ref<vm::CellSlice> cs) {
|
||||
|
@ -588,7 +637,7 @@ td::Result<std::string> to_std_address_or_throw(td::Ref<vm::CellSlice> cs) {
|
|||
if (!tlb::csr_unpack(cs, addr)) {
|
||||
return td::Status::Error("Failed to unpack MsgAddressInt");
|
||||
}
|
||||
return block::StdAddress(addr.workchain_id, addr.address).rserialize();
|
||||
return block::StdAddress(addr.workchain_id, addr.address).rserialize(true);
|
||||
}
|
||||
|
||||
td::Result<std::string> to_std_address(td::Ref<vm::CellSlice> cs) {
|
||||
|
@ -740,7 +789,7 @@ td::Result<tonlib_api::object_ptr<tonlib_api::testWallet_accountState>> to_testW
|
|||
return td::Status::Error("Failed to parse seq_no");
|
||||
}
|
||||
return tonlib_api::make_object<tonlib_api::testWallet_accountState>(
|
||||
raw_state.balance, static_cast<td::uint32>(seqno), to_transaction_id(raw_state.info), raw_state.sync_utime);
|
||||
raw_state.balance, static_cast<td::uint32>(seqno), to_transaction_id(raw_state.info), raw_state.info.gen_utime);
|
||||
}
|
||||
|
||||
td::Result<tonlib_api::object_ptr<tonlib_api::wallet_accountState>> to_wallet_accountState(
|
||||
|
@ -755,7 +804,7 @@ td::Result<tonlib_api::object_ptr<tonlib_api::wallet_accountState>> to_wallet_ac
|
|||
return td::Status::Error("Failed to parse seq_no");
|
||||
}
|
||||
return tonlib_api::make_object<tonlib_api::wallet_accountState>(
|
||||
raw_state.balance, static_cast<td::uint32>(seqno), to_transaction_id(raw_state.info), raw_state.sync_utime);
|
||||
raw_state.balance, static_cast<td::uint32>(seqno), to_transaction_id(raw_state.info), raw_state.info.gen_utime);
|
||||
}
|
||||
|
||||
td::Result<tonlib_api::object_ptr<tonlib_api::testGiver_accountState>> to_testGiver_accountState(
|
||||
|
@ -770,7 +819,7 @@ td::Result<tonlib_api::object_ptr<tonlib_api::testGiver_accountState>> to_testGi
|
|||
return td::Status::Error("Failed to parse seq_no");
|
||||
}
|
||||
return tonlib_api::make_object<tonlib_api::testGiver_accountState>(
|
||||
raw_state.balance, static_cast<td::uint32>(seqno), to_transaction_id(raw_state.info), raw_state.sync_utime);
|
||||
raw_state.balance, static_cast<td::uint32>(seqno), to_transaction_id(raw_state.info), raw_state.info.gen_utime);
|
||||
}
|
||||
|
||||
td::Result<tonlib_api::object_ptr<tonlib_api::generic_AccountState>> to_generic_accountState(
|
||||
|
@ -778,7 +827,7 @@ td::Result<tonlib_api::object_ptr<tonlib_api::generic_AccountState>> to_generic_
|
|||
if (raw_state.code.is_null()) {
|
||||
return tonlib_api::make_object<tonlib_api::generic_accountStateUninited>(
|
||||
tonlib_api::make_object<tonlib_api::uninited_accountState>(raw_state.balance, to_transaction_id(raw_state.info),
|
||||
raw_state.sync_utime));
|
||||
raw_state.info.gen_utime));
|
||||
}
|
||||
|
||||
auto code_hash = raw_state.code->prefetch_ref()->get_hash();
|
||||
|
@ -892,21 +941,21 @@ td::Status TonlibClient::do_request(const tonlib_api::testWallet_init& request,
|
|||
TRY_RESULT(private_key, key_storage_.load_private_key(std::move(input_key)));
|
||||
auto init_message = TestWallet::get_init_message(td::Ed25519::PrivateKey(std::move(private_key.private_key)));
|
||||
return do_request(
|
||||
tonlib_api::raw_sendMessage(tonlib_api::make_object<tonlib_api::accountAddress>(address.rserialize()),
|
||||
tonlib_api::raw_sendMessage(tonlib_api::make_object<tonlib_api::accountAddress>(address.rserialize(true)),
|
||||
vm::std_boc_serialize(init_state).move_as_ok().as_slice().str(),
|
||||
vm::std_boc_serialize(init_message).move_as_ok().as_slice().str()),
|
||||
std::move(promise));
|
||||
}
|
||||
|
||||
td::Status TonlibClient::do_request(const tonlib_api::testWallet_sendGrams& request,
|
||||
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
|
||||
td::Promise<object_ptr<tonlib_api::sendGramsResult>>&& promise) {
|
||||
if (!request.destination_) {
|
||||
return td::Status::Error(400, "Field destination must not be empty");
|
||||
}
|
||||
if (!request.private_key_) {
|
||||
return td::Status::Error(400, "Field private_key must not be empty");
|
||||
}
|
||||
if (request.message_.size() > 124) {
|
||||
if (request.message_.size() > 70) {
|
||||
return td::Status::Error(400, "Message is too long");
|
||||
}
|
||||
TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_));
|
||||
|
@ -914,16 +963,30 @@ td::Status TonlibClient::do_request(const tonlib_api::testWallet_sendGrams& requ
|
|||
TRY_RESULT(input_key, from_tonlib(*request.private_key_));
|
||||
auto address = GenericAccount::get_address(
|
||||
0 /*zerochain*/, TestWallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy())));
|
||||
TRY_RESULT(private_key, key_storage_.load_private_key(std::move(input_key)));
|
||||
return do_request(tonlib_api::raw_sendMessage(
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(address.rserialize()), "",
|
||||
vm::std_boc_serialize(TestWallet::make_a_gift_message(
|
||||
td::Ed25519::PrivateKey(std::move(private_key.private_key)),
|
||||
request.seqno_, request.amount_, request.message_, account_address))
|
||||
.move_as_ok()
|
||||
.as_slice()
|
||||
.str()),
|
||||
std::move(promise));
|
||||
TRY_RESULT(private_key_str, key_storage_.load_private_key(std::move(input_key)));
|
||||
auto private_key = td::Ed25519::PrivateKey(std::move(private_key_str.private_key));
|
||||
std::string init_state;
|
||||
if (request.seqno_ == 0) {
|
||||
TRY_RESULT(public_key, private_key.get_public_key());
|
||||
init_state = vm::std_boc_serialize(TestWallet::get_init_state(public_key)).move_as_ok().as_slice().str();
|
||||
}
|
||||
td::Promise<object_ptr<tonlib_api::ok>> new_promise =
|
||||
[promise = std::move(promise)](td::Result<object_ptr<tonlib_api::ok>> res) mutable {
|
||||
if (res.is_error()) {
|
||||
promise.set_error(res.move_as_error());
|
||||
} else {
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::sendGramsResult>(0));
|
||||
}
|
||||
};
|
||||
return do_request(
|
||||
tonlib_api::raw_sendMessage(
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(address.rserialize(true)), std::move(init_state),
|
||||
vm::std_boc_serialize(TestWallet::make_a_gift_message(private_key, request.seqno_, request.amount_,
|
||||
request.message_, account_address))
|
||||
.move_as_ok()
|
||||
.as_slice()
|
||||
.str()),
|
||||
std::move(new_promise));
|
||||
}
|
||||
|
||||
td::Status TonlibClient::do_request(tonlib_api::testWallet_getAccountState& request,
|
||||
|
@ -956,21 +1019,21 @@ td::Status TonlibClient::do_request(const tonlib_api::wallet_init& request,
|
|||
TRY_RESULT(private_key, key_storage_.load_private_key(std::move(input_key)));
|
||||
auto init_message = Wallet::get_init_message(td::Ed25519::PrivateKey(std::move(private_key.private_key)));
|
||||
return do_request(
|
||||
tonlib_api::raw_sendMessage(tonlib_api::make_object<tonlib_api::accountAddress>(address.rserialize()),
|
||||
tonlib_api::raw_sendMessage(tonlib_api::make_object<tonlib_api::accountAddress>(address.rserialize(true)),
|
||||
vm::std_boc_serialize(init_state).move_as_ok().as_slice().str(),
|
||||
vm::std_boc_serialize(init_message).move_as_ok().as_slice().str()),
|
||||
std::move(promise));
|
||||
}
|
||||
|
||||
td::Status TonlibClient::do_request(const tonlib_api::wallet_sendGrams& request,
|
||||
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
|
||||
td::Promise<object_ptr<tonlib_api::sendGramsResult>>&& promise) {
|
||||
if (!request.destination_) {
|
||||
return td::Status::Error(400, "Field destination must not be empty");
|
||||
}
|
||||
if (!request.private_key_) {
|
||||
return td::Status::Error(400, "Field private_key must not be empty");
|
||||
}
|
||||
if (request.message_.size() > 124) {
|
||||
if (request.message_.size() > 70) {
|
||||
return td::Status::Error(400, "Message is too long");
|
||||
}
|
||||
TRY_RESULT(valid_until, td::narrow_cast_safe<td::uint32>(request.valid_until_));
|
||||
|
@ -979,17 +1042,30 @@ td::Status TonlibClient::do_request(const tonlib_api::wallet_sendGrams& request,
|
|||
TRY_RESULT(input_key, from_tonlib(*request.private_key_));
|
||||
auto address = GenericAccount::get_address(
|
||||
0 /*zerochain*/, Wallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy())));
|
||||
TRY_RESULT(private_key, key_storage_.load_private_key(std::move(input_key)));
|
||||
TRY_RESULT(private_key_str, key_storage_.load_private_key(std::move(input_key)));
|
||||
auto private_key = td::Ed25519::PrivateKey(std::move(private_key_str.private_key));
|
||||
std::string init_state;
|
||||
if (request.seqno_ == 0) {
|
||||
TRY_RESULT(public_key, private_key.get_public_key());
|
||||
init_state = vm::std_boc_serialize(Wallet::get_init_state(public_key)).move_as_ok().as_slice().str();
|
||||
}
|
||||
td::Promise<object_ptr<tonlib_api::ok>> new_promise =
|
||||
[promise = std::move(promise), valid_until](td::Result<object_ptr<tonlib_api::ok>> res) mutable {
|
||||
if (res.is_error()) {
|
||||
promise.set_error(res.move_as_error());
|
||||
} else {
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::sendGramsResult>(valid_until));
|
||||
}
|
||||
};
|
||||
return do_request(
|
||||
tonlib_api::raw_sendMessage(
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(address.rserialize()), "",
|
||||
vm::std_boc_serialize(Wallet::make_a_gift_message(td::Ed25519::PrivateKey(std::move(private_key.private_key)),
|
||||
request.seqno_, valid_until, request.amount_,
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(address.rserialize(true)), std::move(init_state),
|
||||
vm::std_boc_serialize(Wallet::make_a_gift_message(private_key, request.seqno_, valid_until, request.amount_,
|
||||
request.message_, account_address))
|
||||
.move_as_ok()
|
||||
.as_slice()
|
||||
.str()),
|
||||
std::move(promise));
|
||||
std::move(new_promise));
|
||||
}
|
||||
|
||||
td::Status TonlibClient::do_request(tonlib_api::wallet_getAccountState& request,
|
||||
|
@ -1012,23 +1088,31 @@ td::Status TonlibClient::do_request(tonlib_api::wallet_getAccountState& request,
|
|||
|
||||
// TestGiver
|
||||
td::Status TonlibClient::do_request(const tonlib_api::testGiver_sendGrams& request,
|
||||
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
|
||||
td::Promise<object_ptr<tonlib_api::sendGramsResult>>&& promise) {
|
||||
if (!request.destination_) {
|
||||
return td::Status::Error(400, "Field destination must not be empty");
|
||||
}
|
||||
if (request.message_.size() > 124) {
|
||||
if (request.message_.size() > 70) {
|
||||
return td::Status::Error(400, "Message is too long");
|
||||
}
|
||||
TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_));
|
||||
account_address.bounceable = false;
|
||||
td::Promise<object_ptr<tonlib_api::ok>> new_promise =
|
||||
[promise = std::move(promise)](td::Result<object_ptr<tonlib_api::ok>> res) mutable {
|
||||
if (res.is_error()) {
|
||||
promise.set_error(res.move_as_error());
|
||||
} else {
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::sendGramsResult>(0));
|
||||
}
|
||||
};
|
||||
return do_request(tonlib_api::raw_sendMessage(
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(TestGiver::address().rserialize()), "",
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(TestGiver::address().rserialize(true)), "",
|
||||
vm::std_boc_serialize(TestGiver::make_a_gift_message(request.seqno_, request.amount_,
|
||||
request.message_, account_address))
|
||||
.move_as_ok()
|
||||
.as_slice()
|
||||
.str()),
|
||||
std::move(promise));
|
||||
std::move(new_promise));
|
||||
}
|
||||
|
||||
td::Status TonlibClient::do_request(const tonlib_api::testGiver_getAccountState& request,
|
||||
|
@ -1085,21 +1169,16 @@ class TonlibQueryActor : public td::actor::Actor {
|
|||
class GenericSendGrams : public TonlibQueryActor {
|
||||
public:
|
||||
GenericSendGrams(td::actor::ActorShared<TonlibClient> client, tonlib_api::generic_sendGrams send_grams,
|
||||
td::Promise<tonlib_api::object_ptr<tonlib_api::ok>>&& promise)
|
||||
td::Promise<tonlib_api::object_ptr<tonlib_api::sendGramsResult>>&& promise)
|
||||
: TonlibQueryActor(std::move(client)), send_grams_(std::move(send_grams)), promise_(std::move(promise)) {
|
||||
timeout_ = td::Timestamp::in(15);
|
||||
}
|
||||
|
||||
private:
|
||||
tonlib_api::generic_sendGrams send_grams_;
|
||||
td::Promise<tonlib_api::object_ptr<tonlib_api::ok>> promise_;
|
||||
td::Promise<tonlib_api::object_ptr<tonlib_api::sendGramsResult>> promise_;
|
||||
|
||||
enum class SourceAction { Wait, Init, WaitInited, Ok } source_action_ = SourceAction::Wait;
|
||||
tonlib_api::object_ptr<tonlib_api::generic_AccountState> source_state_;
|
||||
block::StdAddress source_address_;
|
||||
td::Timestamp source_next_get_state_;
|
||||
td::Timestamp timeout_;
|
||||
bool has_source_state_query_{false};
|
||||
|
||||
tonlib_api::object_ptr<tonlib_api::generic_AccountState> destination_state_;
|
||||
bool is_destination_bounce_{false};
|
||||
|
@ -1117,6 +1196,7 @@ class GenericSendGrams : public TonlibQueryActor {
|
|||
}
|
||||
|
||||
td::Status do_start_up() {
|
||||
alarm_timestamp() = td::Timestamp::in(15);
|
||||
if (!send_grams_.destination_) {
|
||||
return td::Status::Error(400, "Field destination must not be empty");
|
||||
}
|
||||
|
@ -1161,42 +1241,29 @@ class GenericSendGrams : public TonlibQueryActor {
|
|||
}
|
||||
|
||||
td::Status do_on_source_state(td::Result<tonlib_api::object_ptr<tonlib_api::generic_AccountState>> r_state) {
|
||||
has_source_state_query_ = false;
|
||||
TRY_RESULT(state, std::move(r_state));
|
||||
source_state_ = std::move(state);
|
||||
if (source_action_ == SourceAction::Wait) {
|
||||
source_action_ = SourceAction::Ok;
|
||||
if (false && source_state_->get_id() == tonlib_api::generic_accountStateUninited::ID &&
|
||||
send_grams_.private_key_ && send_grams_.private_key_->key_) {
|
||||
TRY_RESULT(key_bytes, block::PublicKey::parse(send_grams_.private_key_->key_->public_key_));
|
||||
auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key));
|
||||
auto addr = GenericAccount::get_address(0 /*zerochain*/, TestWallet::get_init_state(key));
|
||||
if (addr.addr == source_address_.addr) {
|
||||
source_action_ = SourceAction::Init;
|
||||
send_query(tonlib_api::testWallet_init(clone(send_grams_.private_key_)),
|
||||
[actor_id = actor_id(this)](auto r_res) {
|
||||
send_closure(actor_id, &GenericSendGrams::on_source_init, std::move(r_res));
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (source_action_ == SourceAction::WaitInited) {
|
||||
if (source_state_->get_id() != tonlib_api::generic_accountStateUninited::ID) {
|
||||
source_action_ = SourceAction::Ok;
|
||||
if (source_state_->get_id() == tonlib_api::generic_accountStateUninited::ID && send_grams_.private_key_ &&
|
||||
send_grams_.private_key_->key_) {
|
||||
TRY_RESULT(key_bytes, block::PublicKey::parse(send_grams_.private_key_->key_->public_key_));
|
||||
auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key));
|
||||
|
||||
if (GenericAccount::get_address(0 /*zerochain*/, TestWallet::get_init_state(key)).addr == source_address_.addr) {
|
||||
auto state = ton::move_tl_object_as<tonlib_api::generic_accountStateUninited>(source_state_);
|
||||
source_state_ = tonlib_api::make_object<tonlib_api::generic_accountStateTestWallet>(
|
||||
tonlib_api::make_object<tonlib_api::testWallet_accountState>(-1, 0, nullptr,
|
||||
state->account_state_->sync_utime_));
|
||||
} else if (GenericAccount::get_address(0 /*zerochain*/, Wallet::get_init_state(key)).addr ==
|
||||
source_address_.addr) {
|
||||
auto state = ton::move_tl_object_as<tonlib_api::generic_accountStateUninited>(source_state_);
|
||||
source_state_ = tonlib_api::make_object<tonlib_api::generic_accountStateWallet>(
|
||||
tonlib_api::make_object<tonlib_api::wallet_accountState>(-1, 0, nullptr,
|
||||
state->account_state_->sync_utime_));
|
||||
}
|
||||
}
|
||||
return do_loop();
|
||||
}
|
||||
|
||||
void on_source_init(td::Result<tonlib_api::object_ptr<tonlib_api::ok>> r_ok) {
|
||||
do_on_source_init(std::move(r_ok));
|
||||
}
|
||||
|
||||
td::Status do_on_source_init(td::Result<tonlib_api::object_ptr<tonlib_api::ok>> r_ok) {
|
||||
TRY_RESULT(ok, std::move(r_ok));
|
||||
source_action_ = SourceAction::WaitInited;
|
||||
return do_loop();
|
||||
}
|
||||
|
||||
void on_destination_state(td::Result<tonlib_api::object_ptr<tonlib_api::generic_AccountState>> r_state) {
|
||||
check(do_on_destination_state(std::move(r_state)));
|
||||
}
|
||||
|
@ -1204,34 +1271,18 @@ class GenericSendGrams : public TonlibQueryActor {
|
|||
td::Status do_on_destination_state(td::Result<tonlib_api::object_ptr<tonlib_api::generic_AccountState>> r_state) {
|
||||
TRY_RESULT(state, std::move(r_state));
|
||||
destination_state_ = std::move(state);
|
||||
if (destination_state_->get_id() == tonlib_api::generic_accountStateUninited::ID) {
|
||||
//return td::Status::Error("Transfer to uninited wallet");
|
||||
if (destination_state_->get_id() == tonlib_api::generic_accountStateUninited::ID && is_destination_bounce_ &&
|
||||
!send_grams_.allow_send_to_uninited_) {
|
||||
return td::Status::Error(400, "DANGEROUS_TRANSACTION: Transfer to uninited wallet");
|
||||
}
|
||||
return do_loop();
|
||||
}
|
||||
|
||||
void alarm() override {
|
||||
check(do_loop());
|
||||
check(td::Status::Error("Timeout"));
|
||||
}
|
||||
td::Status do_loop() {
|
||||
if (timeout_.is_in_past()) {
|
||||
return td::Status::Error("Timeout");
|
||||
}
|
||||
alarm_timestamp().relax(timeout_);
|
||||
if (source_action_ == SourceAction::WaitInited && !has_source_state_query_) {
|
||||
if (source_next_get_state_.is_in_past()) {
|
||||
source_next_get_state_ = td::Timestamp::in(1);
|
||||
has_source_state_query_ = true;
|
||||
send_query(tonlib_api::generic_getAccountState(
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(send_grams_.source_->account_address_)),
|
||||
[actor_id = actor_id(this)](auto r_res) {
|
||||
send_closure(actor_id, &GenericSendGrams::on_source_state, std::move(r_res));
|
||||
});
|
||||
} else {
|
||||
alarm_timestamp().relax(source_next_get_state_);
|
||||
}
|
||||
}
|
||||
if (source_action_ != SourceAction::Ok || !destination_state_) {
|
||||
if (!source_state_ || !destination_state_) {
|
||||
return td::Status::OK();
|
||||
}
|
||||
downcast_call(*source_state_,
|
||||
|
@ -1254,7 +1305,10 @@ class GenericSendGrams : public TonlibQueryActor {
|
|||
[&](tonlib_api::generic_accountStateWallet& test_wallet_state) {
|
||||
send_query(tonlib_api::wallet_sendGrams(
|
||||
std::move(send_grams_.private_key_), std::move(send_grams_.destination_),
|
||||
test_wallet_state.account_state_->seqno_, std::numeric_limits<td::uint32>::max(),
|
||||
test_wallet_state.account_state_->seqno_,
|
||||
send_grams_.timeout_ == 0
|
||||
? 60 + test_wallet_state.account_state_->sync_utime_
|
||||
: send_grams_.timeout_ + test_wallet_state.account_state_->sync_utime_,
|
||||
send_grams_.amount_, std::move(send_grams_.message_)),
|
||||
std::move(promise_));
|
||||
stop();
|
||||
|
@ -1272,7 +1326,10 @@ class GenericSendGrams : public TonlibQueryActor {
|
|||
};
|
||||
|
||||
td::Status TonlibClient::do_request(tonlib_api::generic_sendGrams& request,
|
||||
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
|
||||
td::Promise<object_ptr<tonlib_api::sendGramsResult>>&& promise) {
|
||||
if (request.timeout_ < 0 || request.timeout_ > 300) {
|
||||
return td::Status::Error(400, "Invalid timeout: must be between 0 and 300");
|
||||
}
|
||||
auto id = actor_id_++;
|
||||
actors_[id] = td::actor::create_actor<GenericSendGrams>("GenericSendGrams", actor_shared(this, id),
|
||||
std::move(request), std::move(promise));
|
||||
|
@ -1284,7 +1341,7 @@ td::Status TonlibClient::do_request(const tonlib_api::createNewKey& request,
|
|||
TRY_RESULT(key, key_storage_.create_new_key(std::move(request.local_password_), std::move(request.mnemonic_password_),
|
||||
std::move(request.random_extra_seed_)));
|
||||
TRY_RESULT(key_bytes, block::PublicKey::from_bytes(key.public_key.as_slice()));
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key_bytes.serialize(), std::move(key.secret)));
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key_bytes.serialize(true), std::move(key.secret)));
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
|
@ -1321,7 +1378,7 @@ td::Status TonlibClient::do_request(const tonlib_api::importKey& request,
|
|||
TRY_RESULT(key, key_storage_.import_key(std::move(request.local_password_), std::move(request.mnemonic_password_),
|
||||
KeyStorage::ExportedKey{std::move(request.exported_key_->word_list_)}));
|
||||
TRY_RESULT(key_bytes, block::PublicKey::from_bytes(key.public_key.as_slice()));
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key_bytes.serialize(), std::move(key.secret)));
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key_bytes.serialize(true), std::move(key.secret)));
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
|
@ -1350,7 +1407,7 @@ td::Status TonlibClient::do_request(const tonlib_api::importPemKey& request,
|
|||
TRY_RESULT(key, key_storage_.import_pem_key(std::move(request.local_password_), std::move(request.key_password_),
|
||||
KeyStorage::ExportedPemKey{std::move(request.exported_key_->pem_)}));
|
||||
TRY_RESULT(key_bytes, block::PublicKey::from_bytes(key.public_key.as_slice()));
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key_bytes.serialize(), std::move(key.secret)));
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key_bytes.serialize(true), std::move(key.secret)));
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
|
@ -1374,7 +1431,7 @@ td::Status TonlibClient::do_request(const tonlib_api::importEncryptedKey& reques
|
|||
std::move(request.local_password_), std::move(request.key_password_),
|
||||
KeyStorage::ExportedEncryptedKey{std::move(request.exported_encrypted_key_->data_)}));
|
||||
TRY_RESULT(key_bytes, block::PublicKey::from_bytes(key.public_key.as_slice()));
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key_bytes.serialize(), std::move(key.secret)));
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key_bytes.serialize(true), std::move(key.secret)));
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
|
|
|
@ -47,6 +47,9 @@ class TonlibClient : public td::actor::Actor {
|
|||
enum class State { Uninited, Running, Closed } state_ = State::Uninited;
|
||||
td::unique_ptr<TonlibCallback> callback_;
|
||||
Config config_;
|
||||
td::uint32 config_generation_{0};
|
||||
std::string blockchain_name_;
|
||||
bool ignore_cache_{false};
|
||||
|
||||
bool use_callbacks_for_network_{false};
|
||||
td::actor::ActorId<ExtClientOutbound> ext_client_outbound_;
|
||||
|
@ -85,7 +88,7 @@ class TonlibClient : public td::actor::Actor {
|
|||
}
|
||||
}
|
||||
|
||||
void update_last_block_state(LastBlockState state);
|
||||
void update_last_block_state(LastBlockState state, td::uint32 config_generation_);
|
||||
void on_result(td::uint64 id, object_ptr<tonlib_api::Object> response);
|
||||
static bool is_static_request(td::int32 id);
|
||||
static bool is_uninited_request(td::int32 id);
|
||||
|
@ -98,6 +101,8 @@ class TonlibClient : public td::actor::Actor {
|
|||
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::testGiver_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);
|
||||
|
||||
static object_ptr<tonlib_api::Object> do_static_request(tonlib_api::setLogStream& request);
|
||||
|
@ -114,12 +119,10 @@ class TonlibClient : public td::actor::Actor {
|
|||
return td::Status::Error(400, "Function is unsupported");
|
||||
}
|
||||
|
||||
td::Status set_config(std::string config);
|
||||
|
||||
td::Status set_config(object_ptr<tonlib_api::config> config);
|
||||
td::Status do_request(const tonlib_api::init& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Status do_request(const tonlib_api::close& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Status do_request(const tonlib_api::options_setConfig& request,
|
||||
td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Status do_request(tonlib_api::options_setConfig& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
|
||||
td::Status do_request(const tonlib_api::raw_sendMessage& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Status do_request(tonlib_api::raw_getAccountState& request,
|
||||
|
@ -129,23 +132,25 @@ class TonlibClient : public td::actor::Actor {
|
|||
|
||||
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::ok>>&& promise);
|
||||
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::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::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::ok>>&& promise);
|
||||
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::ok>>&& promise);
|
||||
td::Status do_request(tonlib_api::generic_sendGrams& request,
|
||||
td::Promise<object_ptr<tonlib_api::sendGramsResult>>&& 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,
|
||||
|
|
|
@ -49,15 +49,18 @@ td::Ref<vm::Cell> Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& pri
|
|||
td::BigInt256 dest_addr;
|
||||
dest_addr.import_bits(dest_address.addr.as_bitslice());
|
||||
vm::CellBuilder cb;
|
||||
cb.append_cellslice(binary_bitstring_to_cellslice("b{010000100}").move_as_ok())
|
||||
cb.append_cellslice(binary_bitstring_to_cellslice("b{01}").move_as_ok())
|
||||
.store_long(dest_address.bounceable, 1)
|
||||
.append_cellslice(binary_bitstring_to_cellslice("b{000100}").move_as_ok())
|
||||
.store_long(dest_address.workchain, 8)
|
||||
.store_int256(dest_addr, 256);
|
||||
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms));
|
||||
auto message_inner = cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4).store_bytes(message).finalize();
|
||||
td::int8 send_mode = 3;
|
||||
auto message_outer = vm::CellBuilder()
|
||||
.store_long(seqno, 32)
|
||||
.store_long(valid_until, 32)
|
||||
.store_long(1, 8)
|
||||
.store_long(send_mode, 8)
|
||||
.store_ref(message_inner)
|
||||
.finalize();
|
||||
std::string seq_no(4, 0);
|
||||
|
@ -68,8 +71,8 @@ td::Ref<vm::Cell> Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& pri
|
|||
td::Ref<vm::Cell> Wallet::get_init_code() {
|
||||
static auto res = [] {
|
||||
auto serialized_code = td::base64_decode(
|
||||
"te6ccgEEBgEAAAAAaAABFP8A9KQT9KDyyAsBAgEgAgMCAUgEBQCA8oMI1xgg0x/TH/gjErnyY+1E0NMf0//"
|
||||
"RUTG68qED+QFUEEL5EPKi+AACkyDXSpbTB9QC+wDo0aTIyx/L/8ntVAAE0DAAEaCZL9qJoa4WPw==")
|
||||
"te6ccgEEAQEAAAAAVwAAqv8AIN0gggFMl7qXMO1E0NcLH+Ck8mCDCNcYINMf0x8B+CO78mPtRNDTH9P/"
|
||||
"0VExuvKhA/kBVBBC+RDyovgAApMg10qW0wfUAvsA6NGkyMsfy//J7VQ=")
|
||||
.move_as_ok();
|
||||
return vm::std_boc_deserialize(serialized_code).move_as_ok();
|
||||
}();
|
||||
|
|
|
@ -25,9 +25,14 @@ class TonlibCli : public td::actor::Actor {
|
|||
struct Options {
|
||||
bool enable_readline{true};
|
||||
std::string config;
|
||||
std::string name;
|
||||
std::string key_dir{"."};
|
||||
bool use_callbacks_for_network{false};
|
||||
bool use_simple_wallet{false};
|
||||
bool ignore_cache{false};
|
||||
|
||||
bool one_shot{false};
|
||||
std::string cmd;
|
||||
};
|
||||
TonlibCli(Options options) : options_(std::move(options)) {
|
||||
}
|
||||
|
@ -65,8 +70,10 @@ class TonlibCli : public td::actor::Actor {
|
|||
td::actor::ActorShared<TonlibCli> id_;
|
||||
};
|
||||
ref_cnt_++;
|
||||
io_ = td::TerminalIO::create("> ", options_.enable_readline, std::make_unique<Cb>(actor_shared(this)));
|
||||
td::actor::send_closure(io_, &td::TerminalIO::set_log_interface);
|
||||
if (!options_.one_shot) {
|
||||
io_ = td::TerminalIO::create("> ", options_.enable_readline, std::make_unique<Cb>(actor_shared(this)));
|
||||
td::actor::send_closure(io_, &td::TerminalIO::set_log_interface);
|
||||
}
|
||||
|
||||
class TonlibCb : public tonlib::TonlibCallback {
|
||||
public:
|
||||
|
@ -109,13 +116,20 @@ class TonlibCli : public td::actor::Actor {
|
|||
}
|
||||
|
||||
using tonlib_api::make_object;
|
||||
send_query(make_object<tonlib_api::init>(make_object<tonlib_api::options>(options_.config, options_.key_dir,
|
||||
options_.use_callbacks_for_network)),
|
||||
auto config = !options_.config.empty()
|
||||
? make_object<tonlib_api::config>(options_.config, options_.name,
|
||||
options_.use_callbacks_for_network, options_.ignore_cache)
|
||||
: nullptr;
|
||||
send_query(make_object<tonlib_api::init>(make_object<tonlib_api::options>(std::move(config), options_.key_dir)),
|
||||
[](auto r_ok) {
|
||||
LOG_IF(ERROR, r_ok.is_error()) << r_ok.error();
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
void hangup_shared() override {
|
||||
CHECK(ref_cnt_ > 0);
|
||||
ref_cnt_--;
|
||||
|
@ -130,6 +144,26 @@ class TonlibCli : public td::actor::Actor {
|
|||
td::actor::SchedulerContext::get()->stop();
|
||||
}
|
||||
|
||||
void on_wait() {
|
||||
if (options_.one_shot) {
|
||||
LOG(ERROR) << "FAILED (not enough data)";
|
||||
std::_Exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -148,13 +182,15 @@ class TonlibCli : public td::actor::Actor {
|
|||
td::TerminalIO::out() << "help - show this help\n";
|
||||
td::TerminalIO::out() << "genkey - generate new secret key\n";
|
||||
td::TerminalIO::out() << "keys - show all stored keys\n";
|
||||
td::TerminalIO::out() << "exportkey [key_id] - export key\n";
|
||||
td::TerminalIO::out() << "setconfig <path> - set lite server config\n";
|
||||
td::TerminalIO::out() << "unpackaddress <address> - validate and parse address\n";
|
||||
td::TerminalIO::out() << "importkey - import key\n";
|
||||
td::TerminalIO::out() << "exportkey [<key_id>] - export key\n";
|
||||
td::TerminalIO::out() << "setconfig <path> [<name>] [<use_callback>] [<force>] - set lite server config\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";
|
||||
td::TerminalIO::out() << "transfer <from_key_id> <to_key_id> <amount> - transfer <amount> of grams from "
|
||||
td::TerminalIO::out() << "transfer[f] <from_key_id> <to_key_id> <amount> - transfer <amount> of grams from "
|
||||
"<from_key_id> to <to_key_id>.\n"
|
||||
<< "\t<from_key_id> could also be 'giver'\n"
|
||||
<< "\t<to_key_id> could also be 'giver' or smartcontract address\n";
|
||||
|
@ -174,20 +210,36 @@ class TonlibCli : public td::actor::Actor {
|
|||
} else if (cmd == "importkey") {
|
||||
import_key(parser.read_all());
|
||||
} else if (cmd == "setconfig") {
|
||||
set_config(parser.read_word());
|
||||
auto config = parser.read_word();
|
||||
auto name = parser.read_word();
|
||||
auto to_bool = [](td::Slice word) {
|
||||
if (word.empty()) {
|
||||
return false;
|
||||
}
|
||||
if (word == "0" || word == "FALSE" || word == "false") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
auto use_callback = parser.read_word();
|
||||
auto force = parser.read_word();
|
||||
set_config(config, name, to_bool(use_callback), to_bool(force));
|
||||
} 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") {
|
||||
} else if (cmd == "transfer" || cmd == "transferf") {
|
||||
auto from = parser.read_word();
|
||||
auto to = parser.read_word();
|
||||
auto grams = parser.read_word();
|
||||
transfer(from, to, grams);
|
||||
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") {
|
||||
unpack_address(parser.read_word());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -251,6 +303,17 @@ class TonlibCli : public td::actor::Actor {
|
|||
};
|
||||
}
|
||||
|
||||
void unpack_address(td::Slice addr) {
|
||||
send_query(tonlib_api::make_object<tonlib_api::unpackAccountAddress>(addr.str()),
|
||||
[addr = addr.str()](auto r_parsed_addr) mutable {
|
||||
if (r_parsed_addr.is_error()) {
|
||||
LOG(ERROR) << "Failed to parse address: " << r_parsed_addr.error();
|
||||
return;
|
||||
}
|
||||
LOG(ERROR) << to_string(r_parsed_addr.ok());
|
||||
});
|
||||
}
|
||||
|
||||
void generate_key(td::SecureString entropy = {}) {
|
||||
if (entropy.size() < 20) {
|
||||
td::TerminalIO::out() << "Enter some entropy";
|
||||
|
@ -275,6 +338,7 @@ class TonlibCli : public td::actor::Actor {
|
|||
[this, password = std::move(password)](auto r_key) mutable {
|
||||
if (r_key.is_error()) {
|
||||
LOG(ERROR) << "Failed to create new key: " << r_key.error();
|
||||
return;
|
||||
}
|
||||
auto key = r_key.move_as_ok();
|
||||
LOG(ERROR) << to_string(key);
|
||||
|
@ -282,7 +346,7 @@ class TonlibCli : public td::actor::Actor {
|
|||
info.public_key = key->public_key_;
|
||||
info.secret = std::move(key->secret_);
|
||||
keys_.push_back(std::move(info));
|
||||
//export_key(key->public_key_, keys_.size() - 1, std::move(password));
|
||||
export_key(key->public_key_, keys_.size() - 1, std::move(password));
|
||||
store_keys();
|
||||
});
|
||||
}
|
||||
|
@ -305,7 +369,13 @@ class TonlibCli : public td::actor::Actor {
|
|||
auto db = r_db.move_as_ok();
|
||||
td::ConstParser parser(db.as_slice());
|
||||
while (true) {
|
||||
auto public_key = parser.read_word();
|
||||
auto public_key = parser.read_word().str();
|
||||
{
|
||||
auto tmp = td::base64_decode(public_key);
|
||||
if (tmp.is_ok()) {
|
||||
public_key = td::base64url_encode(tmp.move_as_ok());
|
||||
}
|
||||
}
|
||||
auto secret_b64 = parser.read_word();
|
||||
if (secret_b64.empty()) {
|
||||
break;
|
||||
|
@ -313,10 +383,11 @@ class TonlibCli : public td::actor::Actor {
|
|||
auto r_secret = td::base64_decode_secure(secret_b64);
|
||||
if (r_secret.is_error()) {
|
||||
LOG(ERROR) << "Invalid secret database at " << key_db_path();
|
||||
return;
|
||||
}
|
||||
|
||||
KeyInfo info;
|
||||
info.public_key = public_key.str();
|
||||
info.public_key = public_key;
|
||||
info.secret = r_secret.move_as_ok();
|
||||
LOG(INFO) << info.public_key;
|
||||
|
||||
|
@ -324,13 +395,16 @@ class TonlibCli : public td::actor::Actor {
|
|||
}
|
||||
}
|
||||
|
||||
void dump_key(size_t i) {
|
||||
td::TerminalIO::out() << " #" << i << ": Public key: " << keys_[i].public_key << " "
|
||||
<< " Address: "
|
||||
<< to_account_address(PSLICE() << i, false).move_as_ok().address->account_address_ << "\n";
|
||||
}
|
||||
void dump_keys() {
|
||||
td::TerminalIO::out() << "Got " << keys_.size() << " keys"
|
||||
<< "\n";
|
||||
for (size_t i = 0; i < keys_.size(); i++) {
|
||||
td::TerminalIO::out() << " #" << i << ": " << keys_[i].public_key << "\n";
|
||||
td::TerminalIO::out() << " " << to_account_address(PSLICE() << i, false).move_as_ok().address->account_address_
|
||||
<< "\n";
|
||||
dump_key(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -447,12 +521,15 @@ class TonlibCli : public td::actor::Actor {
|
|||
send_query(make_object<tonlib_api::exportKey>(make_object<tonlib_api::inputKey>(
|
||||
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) {
|
||||
[this, key = std::move(key), key_i](auto r_res) {
|
||||
if (r_res.is_error()) {
|
||||
td::TerminalIO::out() << "Can't export key id: [" << key << "] " << r_res.error() << "\n";
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << to_string(r_res.ok());
|
||||
dump_key(key_i);
|
||||
for (auto& word : r_res.ok()->word_list_) {
|
||||
td::TerminalIO::out() << " " << word.as_slice() << "\n";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -496,7 +573,7 @@ class TonlibCli : public td::actor::Actor {
|
|||
});
|
||||
}
|
||||
|
||||
void set_config(td::Slice path) {
|
||||
void set_config(td::Slice path, td::Slice name, bool use_callback, bool ignore_cache) {
|
||||
auto r_data = td::read_file_str(path.str());
|
||||
if (r_data.is_error()) {
|
||||
td::TerminalIO::out() << "Can't read file [" << path << "] : " << r_data.error() << "\n";
|
||||
|
@ -505,13 +582,15 @@ class TonlibCli : public td::actor::Actor {
|
|||
|
||||
auto data = r_data.move_as_ok();
|
||||
using tonlib_api::make_object;
|
||||
send_query(make_object<tonlib_api::options_setConfig>(data), [](auto r_res) {
|
||||
if (r_res.is_error()) {
|
||||
td::TerminalIO::out() << "Can't set config: " << r_res.error() << "\n";
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << to_string(r_res.ok());
|
||||
});
|
||||
send_query(make_object<tonlib_api::options_setConfig>(
|
||||
make_object<tonlib_api::config>(std::move(data), name.str(), use_callback, ignore_cache)),
|
||||
[](auto r_res) {
|
||||
if (r_res.is_error()) {
|
||||
td::TerminalIO::out() << "Can't set config: " << r_res.error() << "\n";
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << to_string(r_res.ok());
|
||||
});
|
||||
}
|
||||
|
||||
void get_state(td::Slice key) {
|
||||
|
@ -519,23 +598,27 @@ class TonlibCli : public td::actor::Actor {
|
|||
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();
|
||||
using tonlib_api::make_object;
|
||||
send_query(make_object<tonlib_api::generic_getAccountState>(
|
||||
ton::move_tl_object_as<tonlib_api::accountAddress>(std::move(address.address))),
|
||||
[](auto r_res) {
|
||||
[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();
|
||||
});
|
||||
}
|
||||
void get_history(td::Slice key) {
|
||||
|
@ -586,54 +669,76 @@ class TonlibCli : public td::actor::Actor {
|
|||
});
|
||||
}
|
||||
|
||||
void transfer(td::Slice from, td::Slice to, td::Slice grams) {
|
||||
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;
|
||||
}
|
||||
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 (from != "giver") {
|
||||
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()](
|
||||
td::Slice password) mutable { this->transfer(std::move(from), std::move(to), grams, password); };
|
||||
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;
|
||||
}
|
||||
transfer(r_from_address.move_as_ok(), r_to_address.move_as_ok(), r_grams.move_as_ok(), "");
|
||||
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) {
|
||||
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()](td::Slice message) mutable {
|
||||
this->transfer(std::move(from), std::move(to), grams, password, message);
|
||||
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);
|
||||
};
|
||||
on_wait();
|
||||
return;
|
||||
}
|
||||
void transfer(Address from, Address to, td::uint64 grams, td::Slice password, td::Slice message) {
|
||||
void transfer(Address from, Address to, td::uint64 grams, td::Slice password, td::Slice message,
|
||||
bool allow_send_to_uninited) {
|
||||
using tonlib_api::make_object;
|
||||
auto key = !from.secret.empty()
|
||||
? make_object<tonlib_api::inputKey>(
|
||||
make_object<tonlib_api::key>(from.public_key, from.secret.copy()), td::SecureString(password))
|
||||
: nullptr;
|
||||
send_query(make_object<tonlib_api::generic_sendGrams>(std::move(key), std::move(from.address),
|
||||
std::move(to.address), grams, message.str()),
|
||||
[](auto r_res) {
|
||||
if (r_res.is_error()) {
|
||||
td::TerminalIO::out() << "Can't transfer: " << r_res.error() << "\n";
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << to_string(r_res.ok());
|
||||
});
|
||||
send_query(
|
||||
make_object<tonlib_api::generic_sendGrams>(std::move(key), std::move(from.address), std::move(to.address),
|
||||
grams, 30, allow_send_to_uninited, message.str()),
|
||||
[this](auto r_res) {
|
||||
if (r_res.is_error()) {
|
||||
td::TerminalIO::out() << "Can't transfer: " << r_res.error() << "\n";
|
||||
on_error();
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << to_string(r_res.ok());
|
||||
on_ok();
|
||||
});
|
||||
}
|
||||
|
||||
void init_simple_wallet(td::Slice key) {
|
||||
|
@ -719,17 +824,28 @@ int main(int argc, char* argv[]) {
|
|||
options.key_dir = arg.str();
|
||||
return td::Status::OK();
|
||||
});
|
||||
p.add_option('E', "execute", "execute one command", [&](td::Slice arg) {
|
||||
options.one_shot = true;
|
||||
options.cmd = arg.str();
|
||||
return td::Status::OK();
|
||||
});
|
||||
p.add_option('v', "verbosity", "set verbosity level", [&](td::Slice arg) {
|
||||
auto verbosity = td::to_integer<int>(arg);
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL) + verbosity);
|
||||
return (verbosity >= 0 && verbosity <= 20) ? td::Status::OK() : td::Status::Error("verbosity must be 0..20");
|
||||
});
|
||||
p.add_option('C', "config", "set lite server config", [&](td::Slice arg) {
|
||||
p.add_option('C', "config-force", "set lite server config, drop config related blockchain cache", [&](td::Slice arg) {
|
||||
TRY_RESULT(data, td::read_file_str(arg.str()));
|
||||
options.config = std::move(data);
|
||||
options.ignore_cache = true;
|
||||
return td::Status::OK();
|
||||
});
|
||||
p.add_option('c', "config", "set lite server config", [&](td::Slice arg) {
|
||||
TRY_RESULT(data, td::read_file_str(arg.str()));
|
||||
options.config = std::move(data);
|
||||
return td::Status::OK();
|
||||
});
|
||||
p.add_option('c', "use-callbacks-for-network", "do not use this", [&]() {
|
||||
p.add_option('n', "use-callbacks-for-network", "do not use this", [&]() {
|
||||
options.use_callbacks_for_network = true;
|
||||
return td::Status::OK();
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue