1
0
Fork 0
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:
ton 2019-10-03 17:04:52 +04:00
parent 841d5ebac2
commit 7ea00ebfcf
89 changed files with 1922 additions and 608 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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