mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
updated tonlib, fixed bugs
updated tonlib fixed bugs in func validator: partial support for hardforks liteserver: support for waitMasterchainBlock prefix transactions: support for gas flat rate
This commit is contained in:
parent
841d5ebac2
commit
7ea00ebfcf
89 changed files with 1922 additions and 608 deletions
|
@ -80,7 +80,7 @@ add_executable(tonlib-cli tonlib/tonlib-cli.cpp)
|
|||
target_link_libraries(tonlib-cli tonlib tdactor tdutils terminal)
|
||||
|
||||
if (NOT CMAKE_CROSSCOMPILING)
|
||||
if (TD_ENABLE_JNI)
|
||||
if (TONLIB_ENABLE_JNI)
|
||||
#FIXME
|
||||
#add_dependencies(tonlib tonlib_generate_java_api)
|
||||
endif()
|
||||
|
|
|
@ -420,9 +420,14 @@ TEST(Tonlib, ParseAddres) {
|
|||
ASSERT_EQ(-1, addr->workchain_id_);
|
||||
ASSERT_EQ(true, addr->bounceable_);
|
||||
ASSERT_EQ(false, addr->testnet_);
|
||||
auto raw = addr->addr_;
|
||||
|
||||
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_);
|
||||
auto addr_str2 = sync_send(client, make_object<tonlib_api::packAccountAddress>(
|
||||
make_object<tonlib_api::unpackedAccountAddress>(-1, false, false, raw)))
|
||||
.move_as_ok();
|
||||
ASSERT_EQ("Uf9Tj6fMJP-OqhAdhKXxq36DL-HYSzCc3-9O6UNzqsgPfdyS", addr_str2->account_address_);
|
||||
}
|
||||
|
||||
TEST(Tonlib, KeysApi) {
|
||||
|
@ -550,7 +555,7 @@ TEST(Tonlib, KeysApi) {
|
|||
make_object<tonlib_api::inputKey>(
|
||||
make_object<tonlib_api::key>(key->public_key_, imported_key->secret_.copy()), new_local_password.copy()),
|
||||
pem_password.copy()));
|
||||
if (r_exported_pem_key.is_error() && r_exported_pem_key.error().message() == "Not supported") {
|
||||
if (r_exported_pem_key.is_error() && r_exported_pem_key.error().message() == "INTERNAL Not supported") {
|
||||
return;
|
||||
}
|
||||
auto exported_pem_key = r_exported_pem_key.move_as_ok();
|
||||
|
|
|
@ -30,7 +30,7 @@ void ExtClient::with_last_block(td::Promise<LastBlockState> promise) {
|
|||
});
|
||||
};
|
||||
if (client_.last_block_actor_.empty()) {
|
||||
return P.set_error(td::Status::Error(500, "No lite clients"));
|
||||
return P.set_error(TonlibError::NoLiteServers());
|
||||
}
|
||||
td::actor::send_closure(client_.last_block_actor_, &LastBlock::get_last_block, std::move(P));
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ void ExtClient::send_raw_query(td::BufferSlice query, td::Promise<td::BufferSlic
|
|||
});
|
||||
};
|
||||
if (client_.andl_ext_client_.empty()) {
|
||||
return P.set_error(td::Status::Error(500, "No lite clients"));
|
||||
return P.set_error(TonlibError::NoLiteServers());
|
||||
}
|
||||
td::actor::send_closure(client_.andl_ext_client_, &ton::adnl::AdnlExtClient::send_query, "query", std::move(query),
|
||||
td::Timestamp::in(10.0), std::move(P));
|
||||
|
|
|
@ -26,6 +26,10 @@
|
|||
|
||||
#include "td/actor/actor.h"
|
||||
#include "td/utils/Container.h"
|
||||
#include "td/utils/Random.h"
|
||||
|
||||
#include "TonlibError.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace tonlib {
|
||||
class LastBlock;
|
||||
|
@ -53,23 +57,37 @@ class ExtClient {
|
|||
void with_last_block(td::Promise<LastBlockState> promise);
|
||||
|
||||
template <class QueryT>
|
||||
void send_query(QueryT query, td::Promise<typename QueryT::ReturnType> promise) {
|
||||
void send_query(QueryT query, td::Promise<typename QueryT::ReturnType> promise, td::int32 seq_no = -1) {
|
||||
auto raw_query = ton::serialize_tl_object(&query, true);
|
||||
LOG(ERROR) << "send query to liteserver: " << to_string(query);
|
||||
td::uint32 tag = td::Random::fast_uint32();
|
||||
VLOG(lite_server) << "send query to liteserver: " << tag << " " << to_string(query);
|
||||
td::BufferSlice liteserver_query =
|
||||
ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_query>(std::move(raw_query)), true);
|
||||
|
||||
send_raw_query(std::move(liteserver_query), [promise = std::move(promise)](td::Result<td::BufferSlice> R) mutable {
|
||||
promise.set_result([&]() -> td::Result<typename QueryT::ReturnType> {
|
||||
TRY_RESULT(data, std::move(R));
|
||||
auto r_error = ton::fetch_tl_object<ton::lite_api::liteServer_error>(data.clone(), true);
|
||||
if (r_error.is_ok()) {
|
||||
auto f = r_error.move_as_ok();
|
||||
return td::Status::Error(f->code_, f->message_);
|
||||
}
|
||||
return ton::fetch_result<QueryT>(std::move(data));
|
||||
}());
|
||||
});
|
||||
if (seq_no >= 0) {
|
||||
auto wait = ton::lite_api::liteServer_waitMasterchainSeqno(seq_no, 5000);
|
||||
VLOG(lite_server) << " with prefix " << to_string(wait);
|
||||
auto prefix = ton::serialize_tl_object(&wait, true);
|
||||
liteserver_query = td::BufferSlice(PSLICE() << prefix.as_slice() << liteserver_query.as_slice());
|
||||
}
|
||||
|
||||
send_raw_query(
|
||||
std::move(liteserver_query), [promise = std::move(promise), tag](td::Result<td::BufferSlice> R) mutable {
|
||||
auto res = [&]() -> td::Result<typename QueryT::ReturnType> {
|
||||
TRY_RESULT_PREFIX(data, std::move(R), TonlibError::LiteServerNetwork());
|
||||
auto r_error = ton::fetch_tl_object<ton::lite_api::liteServer_error>(data.clone(), true);
|
||||
if (r_error.is_ok()) {
|
||||
auto f = r_error.move_as_ok();
|
||||
return TonlibError::LiteServer(f->code_, f->message_);
|
||||
}
|
||||
return ton::fetch_result<QueryT>(std::move(data));
|
||||
}
|
||||
();
|
||||
VLOG_IF(lite_server, res.is_ok())
|
||||
<< "got result from liteserver: " << tag << " " << td::Slice(to_string(res.ok())).truncate(1 << 12);
|
||||
VLOG_IF(lite_server, res.is_error()) << "got error from liteserver: " << tag << " " << res.error();
|
||||
promise.set_result(std::move(res));
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "ExtClientOutbound.h"
|
||||
#include "TonlibError.h"
|
||||
#include <map>
|
||||
namespace tonlib {
|
||||
|
||||
|
@ -40,7 +41,7 @@ class ExtClientOutboundImp : public ExtClientOutbound {
|
|||
void on_query_result(td::int64 id, td::Result<td::BufferSlice> r_data, td::Promise<td::Unit> promise) override {
|
||||
auto it = queries_.find(id);
|
||||
if (it == queries_.end()) {
|
||||
promise.set_error(td::Status::Error(400, "Unknown query id"));
|
||||
promise.set_error(TonlibError::Internal("Unknown query id"));
|
||||
}
|
||||
it->second.set_result(std::move(r_data));
|
||||
queries_.erase(it);
|
||||
|
@ -54,7 +55,7 @@ class ExtClientOutboundImp : public ExtClientOutbound {
|
|||
|
||||
void tear_down() override {
|
||||
for (auto &it : queries_) {
|
||||
it.second.set_error(td::Status::Error(400, "Query cancelled"));
|
||||
it.second.set_error(TonlibError::Cancelled());
|
||||
}
|
||||
queries_.clear();
|
||||
}
|
||||
|
|
|
@ -20,18 +20,19 @@
|
|||
#include "tonlib/utils.h"
|
||||
#include "block/block-auto.h"
|
||||
namespace tonlib {
|
||||
td::Ref<vm::Cell> GenericAccount::get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) {
|
||||
td::Ref<vm::Cell> GenericAccount::get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) noexcept {
|
||||
return vm::CellBuilder()
|
||||
.append_cellslice(binary_bitstring_to_cellslice("b{00110}").move_as_ok())
|
||||
.store_ref(std::move(code))
|
||||
.store_ref(std::move(data))
|
||||
.finalize();
|
||||
}
|
||||
block::StdAddress GenericAccount::get_address(ton::WorkchainId workchain_id, const td::Ref<vm::Cell>& init_state) {
|
||||
block::StdAddress GenericAccount::get_address(ton::WorkchainId workchain_id,
|
||||
const td::Ref<vm::Cell>& init_state) noexcept {
|
||||
return block::StdAddress(workchain_id, init_state->get_hash().bits(), true /*bounce*/);
|
||||
}
|
||||
td::Ref<vm::Cell> GenericAccount::create_ext_message(const block::StdAddress& address, td::Ref<vm::Cell> new_state,
|
||||
td::Ref<vm::Cell> body) {
|
||||
td::Ref<vm::Cell> body) noexcept {
|
||||
block::gen::Message::Record message;
|
||||
/*info*/ {
|
||||
block::gen::CommonMsgInfo::Record_ext_in_msg_info info;
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
namespace tonlib {
|
||||
class GenericAccount {
|
||||
public:
|
||||
static td::Ref<vm::Cell> get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data);
|
||||
static block::StdAddress get_address(ton::WorkchainId workchain_id, const td::Ref<vm::Cell>& init_state);
|
||||
static td::Ref<vm::Cell> get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) noexcept;
|
||||
static block::StdAddress get_address(ton::WorkchainId workchain_id, const td::Ref<vm::Cell>& init_state) noexcept;
|
||||
static td::Ref<vm::Cell> create_ext_message(const block::StdAddress& address, td::Ref<vm::Cell> new_state,
|
||||
td::Ref<vm::Cell> body);
|
||||
td::Ref<vm::Cell> body) noexcept;
|
||||
};
|
||||
} // namespace tonlib
|
||||
|
|
|
@ -22,9 +22,12 @@
|
|||
#include "tonlib/keys/DecryptedKey.h"
|
||||
#include "tonlib/keys/EncryptedKey.h"
|
||||
|
||||
#include "tonlib/TonlibError.h"
|
||||
|
||||
#include "td/utils/filesystem.h"
|
||||
#include "td/utils/port/path.h"
|
||||
#include "td/utils/crypto.h"
|
||||
#include "td/utils/PathView.h"
|
||||
|
||||
namespace tonlib {
|
||||
namespace {
|
||||
|
@ -47,7 +50,7 @@ td::Result<KeyStorage::Key> KeyStorage::save_key(const DecryptedKey &decrypted_k
|
|||
Key res;
|
||||
res.public_key = encrypted_key.public_key.as_octet_string();
|
||||
res.secret = std::move(encrypted_key.secret);
|
||||
TRY_STATUS(kv_->set(to_file_name(res), encrypted_key.encrypted_data));
|
||||
TRY_STATUS_PREFIX(kv_->set(to_file_name(res), encrypted_key.encrypted_data), TonlibError::Internal());
|
||||
return std::move(res);
|
||||
}
|
||||
|
||||
|
@ -68,14 +71,16 @@ td::Result<DecryptedKey> KeyStorage::export_decrypted_key(InputKey input_key) {
|
|||
if (r_encrypted_data.is_ok()) {
|
||||
LOG(WARNING) << "Restore private from deprecated location " << to_file_name_old(input_key.key) << " --> "
|
||||
<< to_file_name(input_key.key);
|
||||
TRY_STATUS(kv_->set(to_file_name(input_key.key), r_encrypted_data.ok()));
|
||||
TRY_STATUS_PREFIX(kv_->set(to_file_name(input_key.key), r_encrypted_data.ok()), TonlibError::Internal());
|
||||
kv_->erase(to_file_name_old(input_key.key)).ignore();
|
||||
}
|
||||
}
|
||||
TRY_RESULT(encrypted_data, std::move(r_encrypted_data));
|
||||
TRY_RESULT_PREFIX(encrypted_data, std::move(r_encrypted_data), TonlibError::KeyUnknown());
|
||||
EncryptedKey encrypted_key{std::move(encrypted_data), td::Ed25519::PublicKey(std::move(input_key.key.public_key)),
|
||||
std::move(input_key.key.secret)};
|
||||
return encrypted_key.decrypt(std::move(input_key.local_password));
|
||||
TRY_RESULT_PREFIX(decrypted_key, encrypted_key.decrypt(std::move(input_key.local_password)),
|
||||
TonlibError::KeyDecrypt());
|
||||
return std::move(decrypted_key);
|
||||
}
|
||||
|
||||
td::Result<KeyStorage::ExportedKey> KeyStorage::export_key(InputKey input_key) {
|
||||
|
@ -93,24 +98,43 @@ td::Result<KeyStorage::PrivateKey> KeyStorage::load_private_key(InputKey input_k
|
|||
}
|
||||
|
||||
td::Status KeyStorage::delete_key(const Key &key) {
|
||||
LOG(WARNING) << "Delete private key stored at " << to_file_name(key);
|
||||
return kv_->erase(to_file_name(key));
|
||||
}
|
||||
|
||||
td::Status KeyStorage::delete_all_keys() {
|
||||
std::vector<std::string> keys;
|
||||
kv_->foreach_key([&](td::Slice key) {
|
||||
if (td::PathView(key).extension().empty()) {
|
||||
keys.push_back(key.str());
|
||||
}
|
||||
});
|
||||
td::Status status;
|
||||
for (auto key : keys) {
|
||||
LOG(WARNING) << "Delete private key stored at " << key;
|
||||
auto err = kv_->erase(key);
|
||||
if (err.is_error() && status.is_ok()) {
|
||||
status = std::move(err);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
td::Result<KeyStorage::Key> KeyStorage::import_key(td::Slice local_password, td::Slice mnemonic_password,
|
||||
ExportedKey exported_key) {
|
||||
TRY_RESULT(mnemonic, Mnemonic::create(std::move(exported_key.mnemonic_words), td::SecureString(mnemonic_password)));
|
||||
if (!mnemonic.is_basic_seed()) {
|
||||
if (mnemonic_password.empty() && mnemonic.is_password_seed()) {
|
||||
return td::Status::Error("Mnemonic password is expected");
|
||||
return TonlibError::NeedMnemonicPassword();
|
||||
}
|
||||
return td::Status::Error("Invalid mnemonic words or password (invalid checksum)");
|
||||
return TonlibError::InvalidMnemonic();
|
||||
}
|
||||
return save_key(DecryptedKey(std::move(mnemonic)), local_password);
|
||||
}
|
||||
|
||||
td::Result<KeyStorage::ExportedPemKey> KeyStorage::export_pem_key(InputKey input_key, td::Slice key_password) {
|
||||
TRY_RESULT(decrypted_key, export_decrypted_key(std::move(input_key)));
|
||||
TRY_RESULT(pem, decrypted_key.private_key.as_pem(key_password));
|
||||
TRY_RESULT_PREFIX(pem, decrypted_key.private_key.as_pem(key_password), TonlibError::Internal());
|
||||
return ExportedPemKey{std::move(pem)};
|
||||
}
|
||||
|
||||
|
@ -120,14 +144,15 @@ td::Result<KeyStorage::Key> KeyStorage::change_local_password(InputKey input_key
|
|||
Key res;
|
||||
res.public_key = std::move(input_key.key.public_key);
|
||||
res.secret = std::move(new_secret);
|
||||
TRY_RESULT(value, kv_->get(to_file_name(input_key.key)));
|
||||
TRY_STATUS(kv_->add(to_file_name(res), value));
|
||||
TRY_RESULT_PREFIX(value, kv_->get(to_file_name(input_key.key)), TonlibError::KeyUnknown());
|
||||
TRY_STATUS_PREFIX(kv_->add(to_file_name(res), value), TonlibError::Internal());
|
||||
return std::move(res);
|
||||
}
|
||||
|
||||
td::Result<KeyStorage::Key> KeyStorage::import_pem_key(td::Slice local_password, td::Slice key_password,
|
||||
ExportedPemKey exported_key) {
|
||||
TRY_RESULT(key, td::Ed25519::PrivateKey::from_pem(exported_key.pem, key_password));
|
||||
TRY_RESULT_PREFIX(key, td::Ed25519::PrivateKey::from_pem(exported_key.pem, key_password),
|
||||
TonlibError::InvalidPemKey());
|
||||
return save_key(DecryptedKey({}, std::move(key)), local_password);
|
||||
}
|
||||
|
||||
|
@ -143,7 +168,7 @@ td::Result<KeyStorage::Key> KeyStorage::import_encrypted_key(td::Slice local_pas
|
|||
ExportedEncryptedKey exported_key) {
|
||||
EncryptedKey encrypted_key{std::move(exported_key.data), td::Ed25519::PublicKey(td::SecureString()),
|
||||
td::SecureString(dummy_secret)};
|
||||
TRY_RESULT(decrypted_key, encrypted_key.decrypt(key_password, false));
|
||||
TRY_RESULT_PREFIX(decrypted_key, encrypted_key.decrypt(key_password, false), TonlibError::KeyDecrypt());
|
||||
return save_key(std::move(decrypted_key), local_password);
|
||||
}
|
||||
|
||||
|
|
|
@ -60,6 +60,7 @@ class KeyStorage {
|
|||
td::Result<Key> change_local_password(InputKey input_key, td::Slice new_local_password);
|
||||
|
||||
td::Status delete_key(const Key& key);
|
||||
td::Status delete_all_keys();
|
||||
|
||||
td::Result<Key> import_key(td::Slice local_password, td::Slice mnemonic_password, ExportedKey exported_key);
|
||||
td::Result<Key> import_pem_key(td::Slice local_password, td::Slice key_password, ExportedPemKey exported_key);
|
||||
|
|
|
@ -42,6 +42,22 @@ class KeyValueDir : public KeyValue {
|
|||
return td::unlink(key.str());
|
||||
}
|
||||
|
||||
void foreach_key(std::function<void(td::Slice)> f) override {
|
||||
int cnt = 0;
|
||||
td::WalkPath::run(directory_, [&](td::Slice path, td::WalkPath::Type type) {
|
||||
cnt++;
|
||||
if (type == td::WalkPath::Type::EnterDir) {
|
||||
if (cnt != 1) {
|
||||
return td::WalkPath::Action::SkipDir;
|
||||
}
|
||||
} else if (type == td::WalkPath::Type::NotDir) {
|
||||
f(path);
|
||||
}
|
||||
|
||||
return td::WalkPath::Action::Continue;
|
||||
}).ignore();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string directory_;
|
||||
|
||||
|
@ -82,6 +98,11 @@ class KeyValueInmemory : public KeyValue {
|
|||
map_.erase(it);
|
||||
return td::Status::OK();
|
||||
}
|
||||
void foreach_key(std::function<void(td::Slice)> f) override {
|
||||
for (auto &it : map_) {
|
||||
f(it.first);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
class Cmp : public std::less<> {
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace tonlib {
|
||||
class KeyValue {
|
||||
public:
|
||||
|
@ -11,6 +13,7 @@ class KeyValue {
|
|||
virtual td::Status set(td::Slice key, td::Slice value) = 0;
|
||||
virtual td::Status erase(td::Slice key) = 0;
|
||||
virtual td::Result<td::SecureString> get(td::Slice key) = 0;
|
||||
virtual void foreach_key(std::function<void(td::Slice)> f) = 0;
|
||||
|
||||
static td::Result<td::unique_ptr<KeyValue>> create_dir(td::CSlice dir);
|
||||
static td::Result<td::unique_ptr<KeyValue>> create_inmemory();
|
||||
|
|
|
@ -18,12 +18,18 @@
|
|||
*/
|
||||
#include "tonlib/LastBlock.h"
|
||||
|
||||
#include "tonlib/utils.h"
|
||||
|
||||
#include "ton/lite-tl.hpp"
|
||||
|
||||
#include "lite-client/lite-client-common.h"
|
||||
|
||||
namespace tonlib {
|
||||
|
||||
// init_state <-> last_key_block
|
||||
// state.valitated_init_state
|
||||
// last_key_block ->
|
||||
//
|
||||
td::StringBuilder& operator<<(td::StringBuilder& sb, const LastBlockState& state) {
|
||||
return sb << td::tag("last_block", state.last_block_id.to_str())
|
||||
<< td::tag("last_key_block", state.last_key_block_id.to_str()) << td::tag("utime", state.utime);
|
||||
|
@ -32,9 +38,10 @@ td::StringBuilder& operator<<(td::StringBuilder& sb, const LastBlockState& state
|
|||
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;
|
||||
}
|
||||
state_.last_block_id = state_.last_key_block_id;
|
||||
|
||||
VLOG(last_block) << "check_init_block: skip - FIXME before release";
|
||||
check_init_block_state_ = QueryState::Done;
|
||||
}
|
||||
|
||||
void LastBlock::get_last_block(td::Promise<LastBlockState> promise) {
|
||||
|
@ -42,9 +49,13 @@ void LastBlock::get_last_block(td::Promise<LastBlockState> promise) {
|
|||
promise.set_error(fatal_error_.clone());
|
||||
return;
|
||||
}
|
||||
|
||||
if (promises_.empty() && get_last_block_state_ == QueryState::Done) {
|
||||
VLOG(last_block) << "sync: start";
|
||||
VLOG(last_block) << "get_last_block: reset";
|
||||
get_last_block_state_ = QueryState::Empty;
|
||||
}
|
||||
|
||||
promises_.push_back(std::move(promise));
|
||||
sync_loop();
|
||||
}
|
||||
|
@ -54,36 +65,43 @@ void LastBlock::sync_loop() {
|
|||
return;
|
||||
}
|
||||
|
||||
update_zero_state(state_.zero_state_id);
|
||||
update_zero_state(state_.zero_state_id, "cache");
|
||||
update_zero_state(ton::ZeroStateIdExt(config_.zero_state_id.id.workchain, config_.zero_state_id.root_hash,
|
||||
config_.zero_state_id.file_hash));
|
||||
config_.zero_state_id.file_hash),
|
||||
"config");
|
||||
|
||||
if (get_mc_info_state_ == QueryState::Empty) {
|
||||
VLOG(last_block) << "get_masterchain_info: start";
|
||||
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();
|
||||
if (check_init_block_state_ == QueryState::Empty) {
|
||||
if (!config_.init_block_id.is_valid()) {
|
||||
check_init_block_state_ = QueryState::Done;
|
||||
VLOG(last_block) << "check_init_block: skip - no init_block in config";
|
||||
} else if (config_.init_block_id == state_.init_block_id) {
|
||||
check_init_block_state_ = QueryState::Done;
|
||||
VLOG(last_block) << "check_init_block: skip - was checked before";
|
||||
} else {
|
||||
check_init_block_state_ = QueryState::Active;
|
||||
check_init_block_stats_.start();
|
||||
if (state_.last_block_id.id.seqno >= config_.init_block_id.id.seqno) {
|
||||
VLOG(last_block) << "check_init_block: start - init_block -> last_block";
|
||||
do_check_init_block(config_.init_block_id, state_.last_key_block_id);
|
||||
} else {
|
||||
VLOG(last_block) << "check_init_block: start - last_block -> init_block";
|
||||
do_check_init_block(state_.last_key_block_id, config_.init_block_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_last_block_state_ == QueryState::Empty && check_init_block_state_ == QueryState::Done) {
|
||||
VLOG(last_block) << "get_last_block: start";
|
||||
get_last_block_stats_.start();
|
||||
get_last_block_state_ = QueryState::Active;
|
||||
do_get_last_block();
|
||||
}
|
||||
|
||||
if (get_mc_info_state_ == QueryState::Done && get_last_block_state_ == QueryState::Done &&
|
||||
|
@ -94,7 +112,8 @@ void LastBlock::sync_loop() {
|
|||
|
||||
void LastBlock::do_get_last_block() {
|
||||
//liteServer.getBlockProof mode:# known_block:tonNode.blockIdExt target_block:mode.0?tonNode.blockIdExt = liteServer.PartialBlockProof;
|
||||
queries_++;
|
||||
VLOG(last_block) << "get_last_block: continue " << state_.last_key_block_id.to_str() << " -> ?";
|
||||
get_last_block_stats_.queries_++;
|
||||
client_.send_query(
|
||||
ton::lite_api::liteServer_getBlockProof(0, create_tl_lite_block_id(state_.last_key_block_id), nullptr),
|
||||
[this, from = state_.last_key_block_id](auto r_block_proof) {
|
||||
|
@ -102,64 +121,69 @@ void LastBlock::do_get_last_block() {
|
|||
});
|
||||
}
|
||||
|
||||
void LastBlock::do_check_init_block(ton::BlockIdExt from) {
|
||||
void LastBlock::do_check_init_block(ton::BlockIdExt from, ton::BlockIdExt to) {
|
||||
VLOG(last_block) << "check_init_block: continue " << from.to_str() << " -> " << to.to_str();
|
||||
//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));
|
||||
});
|
||||
check_init_block_stats_.queries_++;
|
||||
client_.send_query(
|
||||
ton::lite_api::liteServer_getBlockProof(1, create_tl_lite_block_id(from), create_tl_lite_block_id(to)),
|
||||
[this, from, to](auto r_block_proof) { this->on_init_block_proof(from, to, 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) {
|
||||
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(block_proof, std::move(r_block_proof)); //TODO: it is fatal?
|
||||
TRY_RESULT_PREFIX(chain, TRY_VM(process_block_proof(from, std::move(block_proof))),
|
||||
TonlibError::ValidateBlockProof());
|
||||
return std::move(chain);
|
||||
}
|
||||
|
||||
td::Result<std::unique_ptr<block::BlockProofChain>> LastBlock::process_block_proof(
|
||||
ton::BlockIdExt from, ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof> block_proof) {
|
||||
VLOG(last_block) << "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)));
|
||||
if (chain->from != from) {
|
||||
return td::Status::Error(PSLICE() << "block proof chain starts from block " << chain->from.to_str()
|
||||
<< ", not from requested block " << from.to_str());
|
||||
}
|
||||
TRY_STATUS(chain->validate());
|
||||
return std::move(chain);
|
||||
}
|
||||
|
||||
void LastBlock::update_state(block::BlockProofChain& chain) {
|
||||
// Update state_
|
||||
bool is_changed = false;
|
||||
is_changed |= update_mc_last_block(chain->to);
|
||||
if (chain->has_key_block) {
|
||||
is_changed |= update_mc_last_key_block(chain->key_blkid);
|
||||
is_changed |= update_mc_last_block(chain.to);
|
||||
if (chain.has_key_block) {
|
||||
is_changed |= update_mc_last_key_block(chain.key_blkid);
|
||||
}
|
||||
if (chain->has_utime) {
|
||||
update_utime(chain->last_utime);
|
||||
if (chain.has_utime) {
|
||||
update_utime(chain.last_utime);
|
||||
}
|
||||
if (is_changed) {
|
||||
callback_->on_state_changed(state_);
|
||||
}
|
||||
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) {
|
||||
validate_.resume();
|
||||
get_last_block_stats_.validate_.resume();
|
||||
auto r_chain = process_block_proof(from, std::move(r_block_proof));
|
||||
validate_.pause();
|
||||
bool is_ready;
|
||||
get_last_block_stats_.validate_.pause();
|
||||
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_chain.ok()->complete;
|
||||
get_last_block_state_ = QueryState::Empty;
|
||||
VLOG(last_block) << "get_last_block: error " << r_chain.error();
|
||||
on_sync_error(r_chain.move_as_error_suffix("(during last block synchronization)"));
|
||||
return;
|
||||
}
|
||||
if (is_ready) {
|
||||
LOG(INFO) << "End last block synchronization " << state_ << "\n"
|
||||
<< " net queries: " << queries_ << "\n"
|
||||
<< " total: " << total_sync_ << " validation: " << validate_;
|
||||
|
||||
auto chain = r_chain.move_as_ok();
|
||||
CHECK(chain);
|
||||
update_state(*chain);
|
||||
if (chain->complete) {
|
||||
VLOG(last_block) << "get_last_block: done\n" << get_last_block_stats_;
|
||||
get_last_block_state_ = QueryState::Done;
|
||||
sync_loop();
|
||||
} else {
|
||||
|
@ -168,26 +192,26 @@ void LastBlock::on_block_proof(
|
|||
}
|
||||
|
||||
void LastBlock::on_init_block_proof(
|
||||
ton::BlockIdExt from,
|
||||
ton::BlockIdExt from, ton::BlockIdExt to,
|
||||
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof) {
|
||||
validate_.resume();
|
||||
check_init_block_stats_.validate_.resume();
|
||||
auto r_chain = process_block_proof(from, std::move(r_block_proof));
|
||||
validate_.pause();
|
||||
check_init_block_stats_.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()));
|
||||
VLOG(last_block) << "check_init_block: error " << r_chain.error();
|
||||
on_sync_error(r_chain.move_as_error_suffix("(during check init block)"));
|
||||
return;
|
||||
}
|
||||
auto chain = r_chain.move_as_ok();
|
||||
CHECK(chain);
|
||||
update_state(*chain);
|
||||
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;
|
||||
VLOG(last_block) << "check_init_block: done\n" << check_init_block_stats_;
|
||||
check_init_block_state_ = QueryState::Done;
|
||||
sync_loop();
|
||||
} else {
|
||||
do_check_init_block(chain->to);
|
||||
do_check_init_block(chain->to, to);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,28 +219,30 @@ 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_));
|
||||
update_zero_state(create_zero_state_id(info->init_), "masterchain info");
|
||||
// last block is not validated! Do not update it
|
||||
get_mc_info_state_ = QueryState::Done;
|
||||
VLOG(last_block) << "get_masterchain_info: done";
|
||||
} else {
|
||||
get_mc_info_state_ = QueryState::Empty;
|
||||
VLOG(last_block) << "get_masterchain_info: error " << r_info.error();
|
||||
LOG(WARNING) << "Failed liteServer_getMasterchainInfo " << r_info.error();
|
||||
on_sync_error(r_info.move_as_error());
|
||||
}
|
||||
sync_loop();
|
||||
}
|
||||
|
||||
void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id) {
|
||||
void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id, td::Slice source) {
|
||||
if (has_fatal_error()) {
|
||||
return;
|
||||
}
|
||||
if (!zero_state_id.is_valid()) {
|
||||
LOG(ERROR) << "Ignore invalid zero state update";
|
||||
LOG(ERROR) << "Ignore invalid zero state update from " << source;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!state_.zero_state_id.is_valid()) {
|
||||
LOG(INFO) << "Init zerostate: " << zero_state_id.to_str();
|
||||
LOG(INFO) << "Init zerostate from " << source << ": " << zero_state_id.to_str();
|
||||
state_.zero_state_id = std::move(zero_state_id);
|
||||
return;
|
||||
}
|
||||
|
@ -225,8 +251,9 @@ void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id) {
|
|||
return;
|
||||
}
|
||||
|
||||
on_fatal_error(td::Status::Error(PSLICE() << "Masterchain zerostate mismatch: expected: "
|
||||
<< state_.zero_state_id.to_str() << ", found " << zero_state_id.to_str()));
|
||||
on_fatal_error(TonlibError::ValidateZeroState(PSLICE() << "Masterchain zerostate mismatch: expected: "
|
||||
<< state_.zero_state_id.to_str() << ", found "
|
||||
<< zero_state_id.to_str() << " from " << source));
|
||||
}
|
||||
|
||||
bool LastBlock::update_mc_last_block(ton::BlockIdExt mc_block_id) {
|
||||
|
@ -256,6 +283,9 @@ bool LastBlock::update_mc_last_key_block(ton::BlockIdExt mc_key_block_id) {
|
|||
if (!state_.last_key_block_id.is_valid() || state_.last_key_block_id.id.seqno < mc_key_block_id.id.seqno) {
|
||||
state_.last_key_block_id = mc_key_block_id;
|
||||
LOG(INFO) << "Update masterchain key block id: " << state_.last_key_block_id.to_str();
|
||||
//LOG(ERROR) << td::int64(state_.last_key_block_id.id.shard) << " "
|
||||
//<< td::base64_encode(state_.last_key_block_id.file_hash.as_slice()) << " "
|
||||
//<< td::base64_encode(state_.last_key_block_id.root_hash.as_slice());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -268,6 +298,7 @@ void LastBlock::update_utime(td::int64 utime) {
|
|||
}
|
||||
|
||||
void LastBlock::on_sync_ok() {
|
||||
VLOG(last_block) << "sync: ok " << state_;
|
||||
for (auto& promise : promises_) {
|
||||
auto state = state_;
|
||||
promise.set_value(std::move(state));
|
||||
|
@ -275,12 +306,14 @@ void LastBlock::on_sync_ok() {
|
|||
promises_.clear();
|
||||
}
|
||||
void LastBlock::on_sync_error(td::Status status) {
|
||||
VLOG(last_block) << "sync: error " << status;
|
||||
for (auto& promise : promises_) {
|
||||
promise.set_error(status.clone());
|
||||
}
|
||||
promises_.clear();
|
||||
}
|
||||
void LastBlock::on_fatal_error(td::Status status) {
|
||||
VLOG(last_block) << "sync: fatal error " << status;
|
||||
fatal_error_ = std::move(status);
|
||||
on_sync_error(fatal_error_.clone());
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include "tonlib/Config.h"
|
||||
#include "tonlib/ExtClient.h"
|
||||
|
||||
#include "td/utils/tl_helpers.h"
|
||||
|
||||
namespace block {
|
||||
struct BlockProofChain;
|
||||
}
|
||||
|
@ -89,25 +91,44 @@ struct LastBlockState {
|
|||
ton::BlockIdExt last_key_block_id;
|
||||
ton::BlockIdExt last_block_id;
|
||||
td::int64 utime{0};
|
||||
ton::BlockIdExt init_block_id;
|
||||
|
||||
static constexpr td::int32 magic = 0xa7f171a4;
|
||||
enum Version { None = 0, Magic, InitBlock, Next };
|
||||
static constexpr td::int32 version = Version::Next - 1;
|
||||
|
||||
template <class StorerT>
|
||||
void store(StorerT &storer) const {
|
||||
using td::store;
|
||||
using tonlib::store;
|
||||
store(magic, storer);
|
||||
store(version, storer);
|
||||
|
||||
store(zero_state_id, storer);
|
||||
store(last_key_block_id, storer);
|
||||
store(last_block_id, storer);
|
||||
store(utime, storer);
|
||||
store(init_block_id, storer);
|
||||
}
|
||||
|
||||
template <class ParserT>
|
||||
void parse(ParserT &parser) {
|
||||
using td::parse;
|
||||
using tonlib::parse;
|
||||
td::int32 version = 0;
|
||||
if (parser.can_prefetch_int() && parser.prefetch_int_unsafe() == magic) {
|
||||
td::int32 magic;
|
||||
parse(magic, parser);
|
||||
parse(version, parser);
|
||||
}
|
||||
|
||||
parse(zero_state_id, parser);
|
||||
parse(last_key_block_id, parser);
|
||||
parse(last_block_id, parser);
|
||||
parse(utime, parser);
|
||||
if (version >= InitBlock) {
|
||||
parse(init_block_id, parser);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -132,20 +153,36 @@ class LastBlock : public td::actor::Actor {
|
|||
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};
|
||||
QueryState get_mc_info_state_{QueryState::Empty}; // just to check zero state
|
||||
QueryState check_init_block_state_{QueryState::Empty}; // init_block <---> last_key_block (from older to newer)
|
||||
QueryState get_last_block_state_{QueryState::Empty}; // last_key_block_id --> ?
|
||||
|
||||
// stats
|
||||
td::Timer total_sync_;
|
||||
td::Timer validate_;
|
||||
td::uint32 queries_;
|
||||
struct Stats {
|
||||
td::Timer total_sync_;
|
||||
td::Timer validate_;
|
||||
td::uint32 queries_;
|
||||
|
||||
void start() {
|
||||
total_sync_ = td::Timer();
|
||||
validate_ = td::Timer(true);
|
||||
queries_ = 0;
|
||||
}
|
||||
|
||||
friend td::StringBuilder &operator<<(td::StringBuilder &sb, const Stats &stats) {
|
||||
return sb << " net queries: " << stats.queries_ << "\n"
|
||||
<< " total: " << stats.total_sync_ << " validation: " << stats.validate_;
|
||||
}
|
||||
};
|
||||
|
||||
Stats check_init_block_stats_;
|
||||
Stats get_last_block_stats_;
|
||||
|
||||
std::vector<td::Promise<LastBlockState>> promises_;
|
||||
|
||||
void do_check_init_block(ton::BlockIdExt from);
|
||||
void do_check_init_block(ton::BlockIdExt from, ton::BlockIdExt to);
|
||||
void on_init_block_proof(
|
||||
ton::BlockIdExt from,
|
||||
ton::BlockIdExt from, ton::BlockIdExt to,
|
||||
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();
|
||||
|
@ -155,7 +192,11 @@ class LastBlock : public td::actor::Actor {
|
|||
ton::BlockIdExt from,
|
||||
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof);
|
||||
|
||||
void update_zero_state(ton::ZeroStateIdExt zero_state_id);
|
||||
td::Result<std::unique_ptr<block::BlockProofChain>> process_block_proof(
|
||||
ton::BlockIdExt from, ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof> block_proof);
|
||||
|
||||
void update_state(block::BlockProofChain &chain);
|
||||
void update_zero_state(ton::ZeroStateIdExt zero_state_id, td::Slice source);
|
||||
|
||||
bool update_mc_last_block(ton::BlockIdExt mc_block_id);
|
||||
bool update_mc_last_key_block(ton::BlockIdExt mc_key_block_id);
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
*/
|
||||
#include "LastBlockStorage.h"
|
||||
|
||||
#include "tonlib/utils.h"
|
||||
|
||||
#include "td/utils/as.h"
|
||||
#include "td/utils/filesystem.h"
|
||||
#include "td/utils/port/path.h"
|
||||
|
@ -49,6 +51,7 @@ td::Result<LastBlockState> LastBlockStorage::get_state(td::Slice name) {
|
|||
}
|
||||
|
||||
void LastBlockStorage::save_state(td::Slice name, LastBlockState state) {
|
||||
VLOG(last_block) << "Save to cache: " << state;
|
||||
auto x = td::serialize(state);
|
||||
std::string y(x.size() + 8, 0);
|
||||
td::MutableSlice(y).substr(8).copy_from(x);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "Logging.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "auto/tl/tonlib_api.h"
|
||||
|
||||
|
@ -36,11 +37,9 @@ static td::FileLog file_log;
|
|||
static td::TsLog ts_log(&file_log);
|
||||
static td::NullLog null_log;
|
||||
|
||||
td::int32 VERBOSITY_NAME(abc) = VERBOSITY_NAME(DEBUG);
|
||||
td::int32 VERBOSITY_NAME(bcd) = VERBOSITY_NAME(DEBUG);
|
||||
#define ADD_TAG(tag) \
|
||||
{ #tag, &VERBOSITY_NAME(tag) }
|
||||
static const std::map<td::Slice, int *> log_tags{ADD_TAG(abc), ADD_TAG(bcd)};
|
||||
static const std::map<td::Slice, int *> log_tags{ADD_TAG(tonlib_query), ADD_TAG(last_block)};
|
||||
#undef ADD_TAG
|
||||
|
||||
td::Status Logging::set_current_stream(tonlib_api::object_ptr<tonlib_api::LogStream> stream) {
|
||||
|
|
|
@ -22,18 +22,18 @@
|
|||
#include "td/utils/base64.h"
|
||||
|
||||
namespace tonlib {
|
||||
const block::StdAddress& TestGiver::address() {
|
||||
const block::StdAddress& TestGiver::address() noexcept {
|
||||
static block::StdAddress res =
|
||||
block::StdAddress::parse("kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny").move_as_ok();
|
||||
return res;
|
||||
}
|
||||
|
||||
vm::CellHash TestGiver::get_init_code_hash() {
|
||||
vm::CellHash TestGiver::get_init_code_hash() noexcept {
|
||||
return vm::CellHash::from_slice(td::base64_decode("wDkZp0yR4xo+9+BnuAPfGVjBzK6FPzqdv2DwRq3z3KE=").move_as_ok());
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> TestGiver::make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Slice message,
|
||||
const block::StdAddress& dest_address) {
|
||||
const block::StdAddress& dest_address) noexcept {
|
||||
td::BigInt256 dest_addr;
|
||||
dest_addr.import_bits(dest_address.addr.as_bitslice());
|
||||
vm::CellBuilder cb;
|
||||
|
|
|
@ -23,9 +23,9 @@ namespace tonlib {
|
|||
class TestGiver {
|
||||
public:
|
||||
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
|
||||
static const block::StdAddress& address();
|
||||
static vm::CellHash get_init_code_hash();
|
||||
static const block::StdAddress& address() noexcept;
|
||||
static vm::CellHash get_init_code_hash() noexcept;
|
||||
static td::Ref<vm::Cell> make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Slice message,
|
||||
const block::StdAddress& dest_address);
|
||||
const block::StdAddress& dest_address) noexcept;
|
||||
};
|
||||
} // namespace tonlib
|
||||
|
|
|
@ -24,13 +24,13 @@
|
|||
#include "td/utils/base64.h"
|
||||
|
||||
namespace tonlib {
|
||||
td::Ref<vm::Cell> TestWallet::get_init_state(const td::Ed25519::PublicKey& public_key) {
|
||||
td::Ref<vm::Cell> TestWallet::get_init_state(const td::Ed25519::PublicKey& public_key) noexcept {
|
||||
auto code = get_init_code();
|
||||
auto data = get_init_data(public_key);
|
||||
return GenericAccount::get_init_state(std::move(code), std::move(data));
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> TestWallet::get_init_message(const td::Ed25519::PrivateKey& private_key) {
|
||||
td::Ref<vm::Cell> TestWallet::get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept {
|
||||
std::string seq_no(4, 0);
|
||||
auto signature =
|
||||
private_key.sign(vm::CellBuilder().store_bytes(seq_no).finalize()->get_hash().as_slice()).move_as_ok();
|
||||
|
@ -39,7 +39,7 @@ td::Ref<vm::Cell> TestWallet::get_init_message(const td::Ed25519::PrivateKey& pr
|
|||
|
||||
td::Ref<vm::Cell> TestWallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno,
|
||||
td::int64 gramms, td::Slice message,
|
||||
const block::StdAddress& dest_address) {
|
||||
const block::StdAddress& dest_address) noexcept {
|
||||
td::BigInt256 dest_addr;
|
||||
dest_addr.import_bits(dest_address.addr.as_bitslice());
|
||||
vm::CellBuilder cb;
|
||||
|
@ -48,18 +48,22 @@ td::Ref<vm::Cell> TestWallet::make_a_gift_message(const td::Ed25519::PrivateKey&
|
|||
.append_cellslice(binary_bitstring_to_cellslice("b{000100}").move_as_ok())
|
||||
.store_long(dest_address.workchain, 8)
|
||||
.store_int256(dest_addr, 256);
|
||||
td::int32 send_mode = 3;
|
||||
if (gramms == -1) {
|
||||
gramms = 0;
|
||||
send_mode += 128;
|
||||
}
|
||||
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms));
|
||||
cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4);
|
||||
vm::CellString::store(cb, message, 35 * 8).ensure();
|
||||
auto message_inner = cb.finalize();
|
||||
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();
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> TestWallet::get_init_code() {
|
||||
td::Ref<vm::Cell> TestWallet::get_init_code() noexcept {
|
||||
static auto res = [] {
|
||||
auto serialized_code = td::base64_decode(
|
||||
"te6ccgEEAQEAAAAAUwAAov8AIN0gggFMl7qXMO1E0NcLH+Ck8mCBAgDXGCDXCx/tRNDTH9P/"
|
||||
|
@ -70,11 +74,11 @@ td::Ref<vm::Cell> TestWallet::get_init_code() {
|
|||
return res;
|
||||
}
|
||||
|
||||
vm::CellHash TestWallet::get_init_code_hash() {
|
||||
vm::CellHash TestWallet::get_init_code_hash() noexcept {
|
||||
return get_init_code()->get_hash();
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> TestWallet::get_init_data(const td::Ed25519::PublicKey& public_key) {
|
||||
td::Ref<vm::Cell> TestWallet::get_init_data(const td::Ed25519::PublicKey& public_key) noexcept {
|
||||
return vm::CellBuilder().store_long(0, 32).store_bytes(public_key.as_octet_string()).finalize();
|
||||
}
|
||||
} // namespace tonlib
|
||||
|
|
|
@ -27,14 +27,14 @@ namespace tonlib {
|
|||
class TestWallet {
|
||||
public:
|
||||
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
|
||||
static td::Ref<vm::Cell> get_init_state(const td::Ed25519::PublicKey& public_key);
|
||||
static td::Ref<vm::Cell> get_init_message(const td::Ed25519::PrivateKey& private_key);
|
||||
static td::Ref<vm::Cell> get_init_state(const td::Ed25519::PublicKey& public_key) noexcept;
|
||||
static td::Ref<vm::Cell> get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept;
|
||||
static td::Ref<vm::Cell> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno,
|
||||
td::int64 gramms, td::Slice message,
|
||||
const block::StdAddress& dest_address);
|
||||
const block::StdAddress& dest_address) noexcept;
|
||||
|
||||
static td::Ref<vm::Cell> get_init_code();
|
||||
static vm::CellHash get_init_code_hash();
|
||||
static td::Ref<vm::Cell> get_init_data(const td::Ed25519::PublicKey& public_key);
|
||||
static td::Ref<vm::Cell> get_init_code() noexcept;
|
||||
static vm::CellHash get_init_code_hash() noexcept;
|
||||
static td::Ref<vm::Cell> get_init_data(const td::Ed25519::PublicKey& public_key) noexcept;
|
||||
};
|
||||
} // namespace tonlib
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -47,13 +47,13 @@ class TonlibClient : public td::actor::Actor {
|
|||
private:
|
||||
enum class State { Uninited, Running, Closed } state_ = State::Uninited;
|
||||
td::unique_ptr<TonlibCallback> callback_;
|
||||
|
||||
// Config
|
||||
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_;
|
||||
|
||||
// KeyStorage
|
||||
std::shared_ptr<KeyValue> kv_;
|
||||
|
@ -62,6 +62,7 @@ class TonlibClient : public td::actor::Actor {
|
|||
|
||||
// network
|
||||
td::actor::ActorOwn<ton::adnl::AdnlExtClient> raw_client_;
|
||||
td::actor::ActorId<ExtClientOutbound> ext_client_outbound_;
|
||||
td::actor::ActorOwn<LastBlock> raw_last_block_;
|
||||
ExtClient client_;
|
||||
|
||||
|
@ -158,6 +159,7 @@ class TonlibClient : public td::actor::Actor {
|
|||
td::Status do_request(const tonlib_api::exportKey& request,
|
||||
td::Promise<object_ptr<tonlib_api::exportedKey>>&& promise);
|
||||
td::Status do_request(const tonlib_api::deleteKey& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Status do_request(const tonlib_api::deleteAllKeys& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Status do_request(const tonlib_api::importKey& request, td::Promise<object_ptr<tonlib_api::key>>&& promise);
|
||||
|
||||
td::Status do_request(const tonlib_api::exportPemKey& request,
|
||||
|
|
157
tonlib/tonlib/TonlibError.h
Normal file
157
tonlib/tonlib/TonlibError.h
Normal file
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser 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 Library 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 Lesser General Public License for more details.
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/Status.h"
|
||||
#include "common/errorcode.h"
|
||||
// NEED_MNEMONIC_PASSWORD
|
||||
// KEY_UNKNOWN
|
||||
// KEY_DECRYPT
|
||||
// INVALID_MNEMONIC
|
||||
// INVALID_BAG_OF_CELLS
|
||||
// INVALID_PUBLIC_KEY
|
||||
// INVALID_ACCOUNT_ADDRESS
|
||||
// INVALID_CONFIG
|
||||
// INVALID_PEM_KEY
|
||||
// MESSAGE_TOO_LONG
|
||||
// EMPTY_FIELD
|
||||
// INVALID_FIELD
|
||||
// DANGEROUS_TRANSACTION
|
||||
// ACCOUNT_NOT_INITED
|
||||
// ACCOUNT_TYPE_UNKNOWN
|
||||
// ACCOUNT_TYPE_UNEXPECTED
|
||||
// VALIDATE_ACCOUNT_STATE
|
||||
// VALIDATE_TRANSACTION
|
||||
// VALIDATE_ZERO_STATE
|
||||
// VALIDATE_BLOCK_PROOF
|
||||
// NO_LITE_SERVERS
|
||||
// LITE_SERVER_NETWORK
|
||||
// CANCELLED
|
||||
// NOT_ENOUGH_FUNDS
|
||||
// LITE_SERVER
|
||||
// INTERNAL
|
||||
|
||||
namespace tonlib {
|
||||
struct TonlibError {
|
||||
static td::Status NeedMnemonicPassword() {
|
||||
return td::Status::Error(400, "NEED_MNEMONIC_PASSWORD");
|
||||
}
|
||||
static td::Status InvalidMnemonic() {
|
||||
return td::Status::Error(400, "INVALID_MNEMONIC: Invalid mnemonic words or password (invalid checksum)");
|
||||
}
|
||||
static td::Status InvalidBagOfCells(td::Slice comment) {
|
||||
return td::Status::Error(400, PSLICE() << "INVALID_BAG_OF_CELLS: " << comment);
|
||||
}
|
||||
static td::Status InvalidPublicKey() {
|
||||
return td::Status::Error(400, "INVALID_PUBLIC_KEY");
|
||||
}
|
||||
static td::Status InvalidAccountAddress() {
|
||||
return td::Status::Error(400, "INVALID_ACCOUNT_ADDRESS");
|
||||
}
|
||||
static td::Status InvalidConfig(td::Slice reason) {
|
||||
return td::Status::Error(400, PSLICE() << "INVALID_CONFIG: " << reason);
|
||||
}
|
||||
static td::Status InvalidPemKey() {
|
||||
return td::Status::Error(400, "INVALID_PEM_KEY");
|
||||
}
|
||||
static td::Status MessageTooLong() {
|
||||
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");
|
||||
}
|
||||
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);
|
||||
}
|
||||
static td::Status DangerousTransaction(td::Slice reason) {
|
||||
return td::Status::Error(400, PSLICE() << "DANGEROUS_TRANSACTION: " << reason);
|
||||
}
|
||||
static td::Status AccountNotInited() {
|
||||
return td::Status::Error(400, "ACCOUNT_NOT_INITED");
|
||||
}
|
||||
static td::Status AccountTypeUnknown() {
|
||||
return td::Status::Error(400, "ACCOUNT_TYPE_UNKNOWN");
|
||||
}
|
||||
static td::Status AccountTypeUnexpected(td::Slice expected) {
|
||||
return td::Status::Error(400, PSLICE() << "ACCOUNT_TYPE_UNEXPECTED: not a " << expected);
|
||||
}
|
||||
static td::Status Internal() {
|
||||
return td::Status::Error(500, "INTERNAL");
|
||||
}
|
||||
static td::Status Internal(td::Slice message) {
|
||||
return td::Status::Error(500, PSLICE() << "INTERNAL: " << message);
|
||||
}
|
||||
static td::Status KeyUnknown() {
|
||||
return td::Status::Error(500, "KEY_UNKNOWN");
|
||||
}
|
||||
static td::Status KeyDecrypt() {
|
||||
return td::Status::Error(500, "KEY_DECRYPT");
|
||||
}
|
||||
static td::Status ValidateAccountState() {
|
||||
return td::Status::Error(500, "VALIDATE_ACCOUNT_STATE");
|
||||
}
|
||||
static td::Status ValidateTransactions() {
|
||||
return td::Status::Error(500, "VALIDATE_TRANSACTION");
|
||||
}
|
||||
static td::Status ValidateZeroState(td::Slice message) {
|
||||
return td::Status::Error(500, PSLICE() << "VALIDATE_ZERO_STATE: " << message);
|
||||
}
|
||||
static td::Status ValidateBlockProof() {
|
||||
return td::Status::Error(500, "VALIDATE_BLOCK_PROOF");
|
||||
}
|
||||
static td::Status NoLiteServers() {
|
||||
return td::Status::Error(500, "NO_LITE_SERVERS");
|
||||
}
|
||||
static td::Status LiteServerNetwork() {
|
||||
return td::Status::Error(500, "LITE_SERVER_NETWORK");
|
||||
}
|
||||
static td::Status Cancelled() {
|
||||
return td::Status::Error(500, "CANCELLED");
|
||||
}
|
||||
static td::Status NotEnoughFunds() {
|
||||
return td::Status::Error(500, "NOT_ENOUGH_FUNDS");
|
||||
}
|
||||
|
||||
static td::Status LiteServer(td::int32 code, td::Slice message) {
|
||||
auto f = [&](td::Slice code_description) { return LiteServer(code, code_description, message); };
|
||||
switch (ton::ErrorCode(code)) {
|
||||
case ton::ErrorCode::cancelled:
|
||||
return f("CANCELLED");
|
||||
case ton::ErrorCode::failure:
|
||||
return f("FAILURE");
|
||||
case ton::ErrorCode::error:
|
||||
return f("ERROR");
|
||||
case ton::ErrorCode::warning:
|
||||
return f("WARNING");
|
||||
case ton::ErrorCode::protoviolation:
|
||||
return f("PROTOVIOLATION");
|
||||
case ton::ErrorCode::timeout:
|
||||
return f("TIMEOUT");
|
||||
case ton::ErrorCode::notready:
|
||||
return f("NOTREADY");
|
||||
}
|
||||
return f("UNKNOWN");
|
||||
}
|
||||
|
||||
static td::Status LiteServer(td::int32 code, td::Slice code_description, td::Slice message) {
|
||||
return td::Status::Error(500, PSLICE() << "LITE_SERVER_" << code_description << ": " << message);
|
||||
}
|
||||
};
|
||||
} // namespace tonlib
|
|
@ -27,13 +27,13 @@
|
|||
#include <limits>
|
||||
|
||||
namespace tonlib {
|
||||
td::Ref<vm::Cell> Wallet::get_init_state(const td::Ed25519::PublicKey& public_key) {
|
||||
td::Ref<vm::Cell> Wallet::get_init_state(const td::Ed25519::PublicKey& public_key) noexcept {
|
||||
auto code = get_init_code();
|
||||
auto data = get_init_data(public_key);
|
||||
return GenericAccount::get_init_state(std::move(code), std::move(data));
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> Wallet::get_init_message(const td::Ed25519::PrivateKey& private_key) {
|
||||
td::Ref<vm::Cell> Wallet::get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept {
|
||||
td::uint32 seqno = 0;
|
||||
td::uint32 valid_until = std::numeric_limits<td::uint32>::max();
|
||||
auto signature =
|
||||
|
@ -45,7 +45,7 @@ td::Ref<vm::Cell> Wallet::get_init_message(const td::Ed25519::PrivateKey& privat
|
|||
|
||||
td::Ref<vm::Cell> Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno,
|
||||
td::uint32 valid_until, td::int64 gramms, td::Slice message,
|
||||
const block::StdAddress& dest_address) {
|
||||
const block::StdAddress& dest_address) noexcept {
|
||||
td::BigInt256 dest_addr;
|
||||
dest_addr.import_bits(dest_address.addr.as_bitslice());
|
||||
vm::CellBuilder cb;
|
||||
|
@ -54,11 +54,15 @@ td::Ref<vm::Cell> Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& pri
|
|||
.append_cellslice(binary_bitstring_to_cellslice("b{000100}").move_as_ok())
|
||||
.store_long(dest_address.workchain, 8)
|
||||
.store_int256(dest_addr, 256);
|
||||
td::int32 send_mode = 3;
|
||||
if (gramms == -1) {
|
||||
gramms = 0;
|
||||
send_mode += 128;
|
||||
}
|
||||
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms));
|
||||
cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4);
|
||||
vm::CellString::store(cb, message, 35 * 8).ensure();
|
||||
auto message_inner = cb.finalize();
|
||||
td::int8 send_mode = 3;
|
||||
auto message_outer = vm::CellBuilder()
|
||||
.store_long(seqno, 32)
|
||||
.store_long(valid_until, 32)
|
||||
|
@ -70,7 +74,7 @@ td::Ref<vm::Cell> Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& pri
|
|||
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> Wallet::get_init_code() {
|
||||
td::Ref<vm::Cell> Wallet::get_init_code() noexcept {
|
||||
static auto res = [] {
|
||||
auto serialized_code = td::base64_decode(
|
||||
"te6ccgEEAQEAAAAAVwAAqv8AIN0gggFMl7qXMO1E0NcLH+Ck8mCDCNcYINMf0x8B+CO78mPtRNDTH9P/"
|
||||
|
@ -81,11 +85,11 @@ td::Ref<vm::Cell> Wallet::get_init_code() {
|
|||
return res;
|
||||
}
|
||||
|
||||
vm::CellHash Wallet::get_init_code_hash() {
|
||||
vm::CellHash Wallet::get_init_code_hash() noexcept {
|
||||
return get_init_code()->get_hash();
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> Wallet::get_init_data(const td::Ed25519::PublicKey& public_key) {
|
||||
td::Ref<vm::Cell> Wallet::get_init_data(const td::Ed25519::PublicKey& public_key) noexcept {
|
||||
return vm::CellBuilder().store_long(0, 32).store_bytes(public_key.as_octet_string()).finalize();
|
||||
}
|
||||
} // namespace tonlib
|
||||
|
|
|
@ -27,14 +27,14 @@ namespace tonlib {
|
|||
class Wallet {
|
||||
public:
|
||||
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
|
||||
static td::Ref<vm::Cell> get_init_state(const td::Ed25519::PublicKey& public_key);
|
||||
static td::Ref<vm::Cell> get_init_message(const td::Ed25519::PrivateKey& private_key);
|
||||
static td::Ref<vm::Cell> get_init_state(const td::Ed25519::PublicKey& public_key) noexcept;
|
||||
static td::Ref<vm::Cell> get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept;
|
||||
static td::Ref<vm::Cell> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno,
|
||||
td::uint32 valid_until, td::int64 gramms, td::Slice message,
|
||||
const block::StdAddress& dest_address);
|
||||
const block::StdAddress& dest_address) noexcept;
|
||||
|
||||
static td::Ref<vm::Cell> get_init_code();
|
||||
static vm::CellHash get_init_code_hash();
|
||||
static td::Ref<vm::Cell> get_init_data(const td::Ed25519::PublicKey& public_key);
|
||||
static td::Ref<vm::Cell> get_init_code() noexcept;
|
||||
static vm::CellHash get_init_code_hash() noexcept;
|
||||
static td::Ref<vm::Cell> get_init_data(const td::Ed25519::PublicKey& public_key) noexcept;
|
||||
};
|
||||
} // namespace tonlib
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
|
||||
namespace tonlib {
|
||||
td::Result<DecryptedKey> EncryptedKey::decrypt(td::Slice local_password, bool check_public_key) {
|
||||
LOG(ERROR) << "decrypt";
|
||||
if (secret.size() != 32) {
|
||||
return td::Status::Error("Failed to decrypt key: invalid secret size");
|
||||
}
|
||||
|
|
|
@ -192,6 +192,7 @@ class TonlibCli : public td::actor::Actor {
|
|||
td::TerminalIO::out() << "keys - show all stored keys\n";
|
||||
td::TerminalIO::out() << "unpackaddress <address> - validate and parse address\n";
|
||||
td::TerminalIO::out() << "importkey - import key\n";
|
||||
td::TerminalIO::out() << "deletekeys - delete ALL PRIVATE KEYS\n";
|
||||
td::TerminalIO::out() << "exportkey [<key_id>] - export key\n";
|
||||
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";
|
||||
|
@ -213,6 +214,8 @@ class TonlibCli : public td::actor::Actor {
|
|||
try_stop();
|
||||
} else if (cmd == "keys") {
|
||||
dump_keys();
|
||||
} else if (cmd == "deletekeys") {
|
||||
delete_all_keys();
|
||||
} else if (cmd == "exportkey") {
|
||||
export_key(parser.read_word());
|
||||
} else if (cmd == "importkey") {
|
||||
|
@ -415,6 +418,27 @@ class TonlibCli : public td::actor::Actor {
|
|||
dump_key(i);
|
||||
}
|
||||
}
|
||||
void delete_all_keys() {
|
||||
static td::Slice password = td::Slice("I have written down mnemonic words");
|
||||
td::TerminalIO::out() << "You are going to delete ALL PRIVATE KEYS. To confirm enter `" << password << "`\n";
|
||||
cont_ = [this](td::Slice entered) {
|
||||
if (password == entered) {
|
||||
this->do_delete_all_keys();
|
||||
} else {
|
||||
td::TerminalIO::out() << "Your keys left intact\n";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void do_delete_all_keys() {
|
||||
send_query(tonlib_api::make_object<tonlib_api::deleteAllKeys>(), [](auto r_res) {
|
||||
if (r_res.is_error()) {
|
||||
td::TerminalIO::out() << "Something went wrong: " << r_res.error() << "\n";
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << "All your keys have been deleted\n";
|
||||
});
|
||||
}
|
||||
|
||||
std::string key_db_path() {
|
||||
return options_.key_dir + TD_DIR_SLASH + "key_db";
|
||||
|
|
|
@ -20,6 +20,10 @@
|
|||
#include "td/utils/misc.h"
|
||||
#include "vm/cellslice.h"
|
||||
namespace tonlib {
|
||||
int VERBOSITY_NAME(tonlib_query) = VERBOSITY_NAME(INFO);
|
||||
int VERBOSITY_NAME(last_block) = VERBOSITY_NAME(INFO);
|
||||
int VERBOSITY_NAME(lite_server) = VERBOSITY_NAME(INFO);
|
||||
|
||||
td::Result<td::Ref<vm::CellSlice>> binary_bitstring_to_cellslice(td::Slice literal) {
|
||||
unsigned char buff[128];
|
||||
if (!begins_with(literal, "b{") || !ends_with(literal, "}")) {
|
||||
|
|
|
@ -21,6 +21,21 @@
|
|||
#include "ton/ton-types.h"
|
||||
#include "block/block.h"
|
||||
#include "block/block-parse.h"
|
||||
|
||||
namespace tonlib {
|
||||
template <class F>
|
||||
auto try_f(F&& f) noexcept -> decltype(f()) {
|
||||
try {
|
||||
return f();
|
||||
} catch (vm::VmError error) {
|
||||
return td::Status::Error(PSLICE() << "Got a vm exception: " << error.get_msg());
|
||||
}
|
||||
}
|
||||
|
||||
#define TRY_VM(f) try_f([&] { return f; })
|
||||
|
||||
extern int VERBOSITY_NAME(tonlib_query);
|
||||
extern int VERBOSITY_NAME(last_block);
|
||||
extern int VERBOSITY_NAME(lite_server);
|
||||
td::Result<td::Ref<vm::CellSlice>> binary_bitstring_to_cellslice(td::Slice literal);
|
||||
} // namespace tonlib
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue