1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

initial commit

This commit is contained in:
initial commit 2019-09-07 14:03:22 +04:00 committed by vvaltman
commit c2da007f40
1610 changed files with 398047 additions and 0 deletions

156
tonlib/CMakeLists.txt Normal file
View file

@ -0,0 +1,156 @@
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
if (NOT OPENSSL_FOUND)
find_package(OpenSSL REQUIRED)
endif()
set(TONLIB_SOURCE
tonlib/Client.cpp
tonlib/Config.cpp
tonlib/ExtClient.cpp
tonlib/ExtClientLazy.cpp
tonlib/GenericAccount.cpp
tonlib/KeyStorage.cpp
tonlib/LastBlock.cpp
tonlib/TestGiver.cpp
tonlib/TestWallet.cpp
tonlib/TonlibClient.cpp
tonlib/utils.cpp
tonlib/Client.h
tonlib/Config.h
tonlib/ExtClient.h
tonlib/ExtClientLazy.h
tonlib/GenericAccount.h
tonlib/KeyStorage.h
tonlib/LastBlock.h
tonlib/TestGiver.h
tonlib/TestWallet.h
tonlib/TonlibCallback.h
tonlib/TonlibClient.h
tonlib/utils.h
tonlib/keys/bip39.cpp
tonlib/keys/DecryptedKey.cpp
tonlib/keys/EncryptedKey.cpp
tonlib/keys/Mnemonic.cpp
tonlib/keys/SimpleEncryption.cpp
tonlib/keys/bip39.h
tonlib/keys/DecryptedKey.h
tonlib/keys/EncryptedKey.h
tonlib/keys/Mnemonic.h
tonlib/keys/SimpleEncryption.h
)
set(TONLIB_OFFLINE_TEST_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/test/offline.cpp PARENT_SCOPE)
set(TONLIB_ONLINE_TEST_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/test/online.cpp PARENT_SCOPE)
add_library(tonlib STATIC ${TONLIB_SOURCE})
target_include_directories(tonlib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/..
${OPENSSL_INCLUDE_DIR}
)
target_link_libraries(tonlib PRIVATE tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ton_block)
target_link_libraries(tonlib PUBLIC tdutils tl_tonlib_api)
if (TONLIB_ENABLE_JNI AND NOT ANDROID) # jni is available by default on Android
if (NOT JNI_FOUND)
find_package(JNI REQUIRED)
endif()
message(STATUS "Found JNI: ${JNI_INCLUDE_DIRS} ${JNI_LIBRARIES}")
target_include_directories(tonlib PUBLIC ${JAVA_INCLUDE_PATH} ${JAVA_INCLUDE_PATH2})
target_link_libraries(tonlib PUBLIC ${JAVA_JVM_LIBRARY})
endif()
if (NOT CMAKE_CROSSCOMPILING)
if (TD_ENABLE_JNI)
#FIXME
#add_dependencies(tonlib tonlib_generate_java_api)
endif()
endif()
add_library(tonlibjson_private STATIC tonlib/ClientJson.cpp tonlib/ClientJson.h)
target_include_directories(tonlibjson_private PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${TL_TD_AUTO_INCLUDES}>)
target_link_libraries(tonlibjson_private PUBLIC tonlib PRIVATE tl_tonlib_api_json)
set(TONLIB_JSON_HEADERS tonlib/tonlib_client_json.h)
set(TONLIB_JSON_SOURCE tonlib/tonlib_client_json.cpp)
include(GenerateExportHeader)
add_library(tonlibjson SHARED ${TONLIB_JSON_SOURCE} ${TONLIB_JSON_HEADERS})
target_link_libraries(tonlibjson PRIVATE tonlibjson_private)
generate_export_header(tonlibjson EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/tonlib/tonlibjson_export.h)
target_include_directories(tonlibjson PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
if (APPLE)
set_target_properties(tonlibjson PROPERTIES LINK_FLAGS "-Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/tonlibclientjson_export_list")
endif()
add_library(tonlibjson_static STATIC ${TONLIB_JSON_SOURCE} ${TONLIB_JSON_HEADERS})
target_link_libraries(tonlibjson_static PRIVATE tonlibjson_private)
target_compile_definitions(tonlibjson_static PUBLIC TONLIBJSON_STATIC_DEFINE)
target_include_directories(tonlibjson_static PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
add_library(TonlibJson INTERFACE)
target_link_libraries(TonlibJson INTERFACE tonlibjson)
add_library(Tonlib::TonlibJson ALIAS TonlibJson)
add_library(Tonlib INTERFACE)
target_link_libraries(Tonlib INTERFACE tonlib)
add_library(Tonlib::Tonlib ALIAS Tonlib)
install(TARGETS tonlibjson TonlibJson EXPORT Tonlib
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION include
)
if (NOT TON_USE_ABSEIL)
install(TARGETS tdnet keys crc32c tdactor adnllite tl_api tl-utils tl_lite_api tl-lite-utils ton_crypto ton_block tdutils tl_tonlib_api tonlib Tonlib EXPORT Tonlib
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION include
)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/tonlib/Client.h DESTINATION include/tonlib/)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../tl/generate/auto/tl/tonlib_api.h DESTINATION include/auto/tl/)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../tl/tl/TlObject.h DESTINATION include/tl/)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../tdutils/td/utils/int_types.h DESTINATION include/td/utils/)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../tdutils/td/utils/Slice-decl.h DESTINATION include/td/utils/)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../tdutils/td/utils/Slice.h DESTINATION include/td/utils/)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../tdutils/td/utils/common.h DESTINATION include/td/utils/)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../tdutils/td/utils/unique_ptr.h DESTINATION include/td/utils/)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../tdutils/td/utils/check.h DESTINATION include/td/utils/)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../tdutils/td/utils/SharedSlice.h DESTINATION include/td/utils/)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../tdutils/td/utils/port/platform.h DESTINATION include/td/utils/port)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/../tdutils/td/utils/config.h DESTINATION include/td/utils/)
endif()
install(FILES ${TONLIB_JSON_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/tonlib/tonlibjson_export.h DESTINATION include/tonlib/)
install(EXPORT Tonlib
FILE TonlibTargets.cmake
NAMESPACE Tonlib::
DESTINATION lib/cmake/Tonlib
)
include(CMakePackageConfigHelpers)
write_basic_package_version_file("TonlibConfigVersion.cmake"
VERSION ${TON_VERSION}
COMPATIBILITY ExactVersion
)
install(FILES "TonlibConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/TonlibConfigVersion.cmake"
DESTINATION lib/cmake/Tonlib
)
# Add SOVERSION to shared libraries
set_property(TARGET tonlibjson PROPERTY SOVERSION ${TON_VERSION})

414
tonlib/test/offline.cpp Normal file
View file

@ -0,0 +1,414 @@
/*
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
*/
#include "fift/Fift.h"
#include "fift/words.h"
#include "fift/utils.h"
#include "block/block.h"
#include "block/block-auto.h"
#include "vm/cells.h"
#include "vm/boc.h"
#include "vm/cells/MerkleProof.h"
#include "tonlib/utils.h"
#include "tonlib/TestGiver.h"
#include "tonlib/TestWallet.h"
#include "tonlib/GenericAccount.h"
#include "tonlib/TonlibClient.h"
#include "tonlib/Client.h"
#include "auto/tl/ton_api_json.h"
#include "auto/tl/tonlib_api_json.h"
#include "td/utils/benchmark.h"
#include "td/utils/filesystem.h"
#include "td/utils/optional.h"
#include "td/utils/port/path.h"
#include "td/utils/PathView.h"
#include "td/utils/tests.h"
// KeyManager
#include "tonlib/keys/bip39.h"
#include "tonlib/keys/DecryptedKey.h"
#include "tonlib/keys/EncryptedKey.h"
#include "tonlib/keys/Mnemonic.h"
#include "tonlib/keys/SimpleEncryption.h"
using namespace tonlib;
std::string current_dir() {
return td::PathView(td::realpath(__FILE__).move_as_ok()).parent_dir().str();
}
std::string load_source(std::string name) {
return td::read_file_str(current_dir() + "../../crypto/" + name).move_as_ok();
}
td::Ref<vm::Cell> get_test_wallet_source() {
std::string code = R"ABCD(
SETCP0 DUP IFNOTRET INC 32 THROWIF // return if recv_internal, fail unless recv_external
512 INT LDSLICEX DUP 32 PLDU // sign cs cnt
c4 PUSHCTR CTOS 32 LDU 256 LDU ENDS // sign cs cnt cnt' pubk
s1 s2 XCPU // sign cs cnt pubk cnt' cnt
EQUAL 33 THROWIFNOT // ( seqno mismatch? )
s2 PUSH HASHSU // sign cs cnt pubk hash
s0 s4 s4 XC2PU // pubk cs cnt hash sign pubk
CHKSIGNU // pubk cs cnt ?
34 THROWIFNOT // signature mismatch
ACCEPT
SWAP 32 LDU NIP
DUP SREFS IF:<{
8 LDU LDREF // pubk cnt mode msg cs
s0 s2 XCHG SENDRAWMSG // pubk cnt cs ; ( message sent )
}>
ENDS
INC NEWC 32 STU 256 STU ENDC c4 POPCTR
)ABCD";
return fift::compile_asm(code).move_as_ok();
}
TEST(Tonlib, TestWallet) {
CHECK(get_test_wallet_source()->get_hash() == TestWallet::get_init_code()->get_hash());
auto fift_output = fift::mem_run_fift(load_source("smartcont/new-wallet.fif"), {"aba", "0"}).move_as_ok();
auto new_wallet_pk = fift_output.source_lookup.read_file("new-wallet.pk").move_as_ok().data;
auto new_wallet_query = fift_output.source_lookup.read_file("new-wallet-query.boc").move_as_ok().data;
auto new_wallet_addr = fift_output.source_lookup.read_file("new-wallet.addr").move_as_ok().data;
td::Ed25519::PrivateKey priv_key{td::SecureString{new_wallet_pk}};
auto pub_key = priv_key.get_public_key().move_as_ok();
auto init_state = TestWallet::get_init_state(pub_key);
auto init_message = TestWallet::get_init_message(priv_key);
auto address = GenericAccount::get_address(0, init_state);
CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32));
td::Ref<vm::Cell> res = GenericAccount::create_ext_message(address, init_state, init_message);
LOG(ERROR) << "-------";
vm::load_cell_slice(res).print_rec(std::cerr);
LOG(ERROR) << "-------";
vm::load_cell_slice(vm::std_boc_deserialize(new_wallet_query).move_as_ok()).print_rec(std::cerr);
CHECK(vm::std_boc_deserialize(new_wallet_query).move_as_ok()->get_hash() == res->get_hash());
}
TEST(Tonlib, TestGiver) {
auto address =
block::StdAddress::parse("-1:60c04141c6a7b96d68615e7a91d265ad0f3a9a922e9ae9c901d4fa83f5d3c0d0").move_as_ok();
LOG(ERROR) << address.bounceable;
auto fift_output = fift::mem_run_fift(load_source("smartcont/testgiver.fif"),
{"aba", address.rserialize(), "0", "6.666", "wallet-query"})
.move_as_ok();
LOG(ERROR) << fift_output.output;
auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data;
auto res = GenericAccount::create_ext_message(TestGiver::address(), {},
TestGiver::make_a_gift_message(0, 1000000000ll * 6666 / 1000, address));
vm::CellSlice(vm::NoVm(), res).print_rec(std::cerr);
CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == res->get_hash());
}
TEST(Tonlib, Address) {
auto a = block::StdAddress::parse("-1:538fa7cc24ff8eaa101d84a5f1ab7e832fe1d84b309cdfef4ee94373aac80f7d").move_as_ok();
auto b = block::StdAddress::parse("Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX").move_as_ok();
auto c = block::StdAddress::parse("Ef9Tj6fMJP-OqhAdhKXxq36DL-HYSzCc3-9O6UNzqsgPfYFX").move_as_ok();
CHECK(a == b);
CHECK(a == c);
CHECK(block::StdAddress::parse("Ef9Tj6fMJp-OqhAdhKXxq36DL-HYSzCc3-9O6UNzqsgPfYFX").is_error());
CHECK(block::StdAddress::parse("Ef9Tj6fMJp+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX").is_error());
CHECK(block::StdAddress::parse(a.rserialize()).move_as_ok() == a);
}
static auto sync_send = [](auto &client, auto query) {
using ReturnTypePtr = typename std::decay_t<decltype(*query)>::ReturnType;
using ReturnType = typename ReturnTypePtr::element_type;
client.send({1, std::move(query)});
while (true) {
auto response = client.receive(100);
if (response.object) {
CHECK(response.id == 1);
if (response.object->get_id() == tonlib_api::error::ID) {
auto error = tonlib_api::move_object_as<tonlib_api::error>(response.object);
return td::Result<ReturnTypePtr>(td::Status::Error(error->code_, error->message_));
}
return td::Result<ReturnTypePtr>(tonlib_api::move_object_as<ReturnType>(response.object));
}
}
};
TEST(Tonlib, InitClose) {
using tonlib_api::make_object;
{
Client client;
sync_send(client, make_object<tonlib_api::close>()).ensure();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>("", "."))).ensure_error();
}
{
Client client;
sync_send(client, make_object<tonlib_api::init>(nullptr)).ensure_error();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>("fdajkfldsjkafld", ".")))
.ensure_error();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>("", "fdhskfds"))).ensure_error();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>("", "."))).ensure();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>("", "."))).ensure_error();
td::Slice bad_config = R"abc(
{
"@type": "config.global",
"liteclients": [ ]
}
)abc";
sync_send(client, make_object<tonlib_api::options_setConfig>(bad_config.str())).ensure_error();
sync_send(client, make_object<tonlib_api::testGiver_getAccountState>()).ensure_error();
sync_send(client, make_object<tonlib_api::close>()).ensure();
sync_send(client, make_object<tonlib_api::close>()).ensure_error();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>("", "."))).ensure_error();
}
}
TEST(Tonlib, SimpleEncryption) {
std::string secret = "secret";
{
std::string data = "some private data";
std::string wrong_secret = "wrong secret";
auto encrypted_data = SimpleEncryption::encrypt_data(data, secret);
LOG(ERROR) << encrypted_data.size();
auto decrypted_data = SimpleEncryption::decrypt_data(encrypted_data, secret).move_as_ok();
CHECK(data == decrypted_data);
SimpleEncryption::decrypt_data(encrypted_data, wrong_secret).ensure_error();
SimpleEncryption::decrypt_data("", secret).ensure_error();
SimpleEncryption::decrypt_data(std::string(32, 'a'), secret).ensure_error();
SimpleEncryption::decrypt_data(std::string(33, 'a'), secret).ensure_error();
SimpleEncryption::decrypt_data(std::string(64, 'a'), secret).ensure_error();
SimpleEncryption::decrypt_data(std::string(128, 'a'), secret).ensure_error();
}
for (size_t i = 0; i < 255; i++) {
auto data = td::rand_string('a', 'z', static_cast<int>(i));
auto encrypted_data = SimpleEncryption::encrypt_data(data, secret);
auto decrypted_data = SimpleEncryption::decrypt_data(encrypted_data, secret).move_as_ok();
CHECK(data == decrypted_data);
}
}
class MnemonicBench : public td::Benchmark {
public:
std::string get_description() const override {
return "mnemonic is_password_seed";
}
void start_up() override {
Mnemonic::Options options;
options.password = td::SecureString("qwerty");
mnemonic_ = Mnemonic::create_new(std::move(options)).move_as_ok();
}
void run(int n) override {
int x = 0;
for (int i = 0; i < n; i++) {
x += mnemonic_.value().is_password_seed();
}
td::do_not_optimize_away(x);
}
td::optional<Mnemonic> mnemonic_;
};
TEST(Tonlib, Mnemonic) {
td::bench(MnemonicBench());
//for (int i = 0; i < 20; i++) {
//td::PerfWarningTimer perf("Mnemonic::create", 0.01);
//Mnemonic::Options options;
//options.password = td::SecureString("qwerty");
//Mnemonic::create_new(std::move(options)).move_as_ok();
//}
// FIXME
//auto tmp = std::vector<td::SecureString>{"hello", "world"};
//CHECK(tmp[0].as_slice() == "hello");
auto a = Mnemonic::create(td::SecureString(" Hello, . $^\n# World! "), td::SecureString("cucumber")).move_as_ok();
auto get_word_list = [] {
std::vector<td::SecureString> words;
words.emplace_back("hello");
words.emplace_back("world");
return words;
};
auto b = Mnemonic::create(get_word_list(), td::SecureString("cucumber")).move_as_ok();
CHECK(a.get_words() == b.get_words());
CHECK(a.get_words() == get_word_list());
Mnemonic::Options options;
options.password = td::SecureString("qwerty");
auto password = options.password.copy();
auto c = Mnemonic::create_new(std::move(options)).move_as_ok();
auto d = Mnemonic::create(c.get_words(), std::move(password)).move_as_ok();
CHECK(c.to_private_key().as_octet_string() == d.to_private_key().as_octet_string());
}
TEST(Tonlib, Keys) {
auto a = Mnemonic::create(td::SecureString(" Hello, . $^\n# World! "), td::SecureString("cucumber")).move_as_ok();
DecryptedKey decrypted_key(std::move(a));
EncryptedKey encrypted_key = decrypted_key.encrypt("qwerty");
auto other_decrypted_key = encrypted_key.decrypt("qwerty").move_as_ok();
encrypted_key.decrypt("abcde").ensure_error();
CHECK(decrypted_key.mnemonic_words == other_decrypted_key.mnemonic_words);
CHECK(decrypted_key.private_key.as_octet_string() == other_decrypted_key.private_key.as_octet_string());
}
TEST(Tonlib, KeysApi) {
using tonlib_api::make_object;
Client client;
// init
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>("", "."))).ensure();
auto local_password = td::SecureString("local password");
auto mnemonic_password = td::SecureString("mnemonic password");
{
auto key = sync_send(client, make_object<tonlib_api::createNewKey>(local_password.copy(), td::SecureString{}))
.move_as_ok();
}
//createNewKey local_password:bytes mnemonic_password:bytes = Key;
auto key = sync_send(client, make_object<tonlib_api::createNewKey>(local_password.copy(), mnemonic_password.copy()))
.move_as_ok();
sync_send(client, make_object<tonlib_api::exportKey>(make_object<tonlib_api::inputKey>(
make_object<tonlib_api::key>(key->public_key_, key->secret_.copy()),
td::SecureString("wrong password"))))
.ensure_error();
//exportKey input_key:inputKey = ExportedKey;
auto exported_key =
sync_send(client,
make_object<tonlib_api::exportKey>(make_object<tonlib_api::inputKey>(
make_object<tonlib_api::key>(key->public_key_, key->secret_.copy()), local_password.copy())))
.move_as_ok();
LOG(ERROR) << to_string(exported_key);
auto copy_word_list = [&] {
std::vector<td::SecureString> word_list_copy;
for (auto &w : exported_key->word_list_) {
word_list_copy.push_back(w.copy());
}
return word_list_copy;
};
//changeLocalPassword input_key:inputKey new_local_password:bytes = Key;
auto new_key =
sync_send(client,
make_object<tonlib_api::changeLocalPassword>(
make_object<tonlib_api::inputKey>(
make_object<tonlib_api::key>(key->public_key_, key->secret_.copy()), local_password.copy()),
td::SecureString("tmp local password")))
.move_as_ok();
sync_send(client,
make_object<tonlib_api::exportKey>(make_object<tonlib_api::inputKey>(
make_object<tonlib_api::key>(key->public_key_, new_key->secret_.copy()), local_password.copy())))
.ensure_error();
auto exported_key2 = sync_send(client, make_object<tonlib_api::exportKey>(make_object<tonlib_api::inputKey>(
make_object<tonlib_api::key>(key->public_key_, new_key->secret_.copy()),
td::SecureString("tmp local password"))))
.move_as_ok();
CHECK(exported_key2->word_list_ == exported_key->word_list_);
//importKey local_password:bytes mnemonic_password:bytes exported_key:exportedKey = Key;
auto new_local_password = td::SecureString("new_local_password");
// import already existed key
sync_send(client, make_object<tonlib_api::importKey>(new_local_password.copy(), mnemonic_password.copy(),
make_object<tonlib_api::exportedKey>(copy_word_list())))
.ensure_error();
{
auto export_password = td::SecureString("export password");
auto wrong_export_password = td::SecureString("wrong_export password");
auto exported_encrypted_key =
sync_send(client, make_object<tonlib_api::exportEncryptedKey>(
make_object<tonlib_api::inputKey>(
make_object<tonlib_api::key>(key->public_key_, new_key->secret_.copy()),
td::SecureString("tmp local password")),
export_password.copy()))
.move_as_ok();
sync_send(client, make_object<tonlib_api::deleteKey>(key->public_key_)).move_as_ok();
sync_send(client, make_object<tonlib_api::importEncryptedKey>(
new_local_password.copy(), wrong_export_password.copy(),
make_object<tonlib_api::exportedEncryptedKey>(exported_encrypted_key->data_.copy())))
.ensure_error();
auto imported_encrypted_key =
sync_send(client, make_object<tonlib_api::importEncryptedKey>(
new_local_password.copy(), export_password.copy(),
make_object<tonlib_api::exportedEncryptedKey>(exported_encrypted_key->data_.copy())))
.move_as_ok();
CHECK(imported_encrypted_key->public_key_ == key->public_key_);
}
//deleteKey public_key:bytes = Ok;
sync_send(client, make_object<tonlib_api::deleteKey>(key->public_key_)).move_as_ok();
auto err1 = sync_send(client, make_object<tonlib_api::importKey>(
new_local_password.copy(), td::SecureString("wrong password"),
make_object<tonlib_api::exportedKey>(copy_word_list())))
.move_as_error();
auto err2 =
sync_send(client, make_object<tonlib_api::importKey>(new_local_password.copy(), td::SecureString(),
make_object<tonlib_api::exportedKey>(copy_word_list())))
.move_as_error();
LOG(INFO) << err1 << " | " << err2;
auto imported_key =
sync_send(client, make_object<tonlib_api::importKey>(new_local_password.copy(), mnemonic_password.copy(),
make_object<tonlib_api::exportedKey>(copy_word_list())))
.move_as_ok();
CHECK(imported_key->public_key_ == key->public_key_);
CHECK(imported_key->secret_ != key->secret_);
//exportPemKey input_key:inputKey key_password:bytes = ExportedPemKey;
auto pem_password = td::SecureString("pem password");
auto r_exported_pem_key = sync_send(
client,
make_object<tonlib_api::exportPemKey>(
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") {
return;
}
auto exported_pem_key = r_exported_pem_key.move_as_ok();
LOG(ERROR) << to_string(exported_pem_key);
//importPemKey exported_key:exportedPemKey key_password:bytes = Key;
sync_send(client, make_object<tonlib_api::importPemKey>(
new_local_password.copy(), pem_password.copy(),
make_object<tonlib_api::exportedPemKey>(exported_pem_key->pem_.copy())))
.ensure_error();
sync_send(client, make_object<tonlib_api::deleteKey>(key->public_key_)).move_as_ok();
sync_send(client, make_object<tonlib_api::importPemKey>(
new_local_password.copy(), td::SecureString("wrong pem password"),
make_object<tonlib_api::exportedPemKey>(exported_pem_key->pem_.copy())))
.ensure_error();
auto new_imported_key = sync_send(client, make_object<tonlib_api::importPemKey>(
new_local_password.copy(), pem_password.copy(),
make_object<tonlib_api::exportedPemKey>(exported_pem_key->pem_.copy())))
.move_as_ok();
CHECK(new_imported_key->public_key_ == key->public_key_);
CHECK(new_imported_key->secret_ != key->secret_);
}

336
tonlib/test/online.cpp Normal file
View file

@ -0,0 +1,336 @@
/*
This file is part of TON Blockchain source code.
TON Blockchain is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
TON Blockchain is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
You must obey the GNU General Public License in all respects for all
of the code used other than OpenSSL. If you modify file(s) with this
exception, you may extend this exception to your version of the file(s),
but you are not obligated to do so. If you do not wish to do so, delete this
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "adnl/adnl-ext-client.h"
#include "tl-utils/tl-utils.hpp"
#include "auto/tl/ton_api_json.h"
#include "auto/tl/tonlib_api_json.h"
#include "tl/tl_json.h"
#include "ton/ton-types.h"
#include "ton/ton-tl.hpp"
#include "block/block.h"
#include "block/block-auto.h"
#include "tonlib/GenericAccount.h"
#include "tonlib/TestGiver.h"
#include "tonlib/TestWallet.h"
#include "tonlib/LastBlock.h"
#include "tonlib/ExtClient.h"
#include "tonlib/utils.h"
#include "tonlib/TonlibCallback.h"
#include "tonlib/Client.h"
#include "vm/cells.h"
#include "vm/boc.h"
#include "vm/cells/MerkleProof.h"
#include "td/utils/Container.h"
#include "td/utils/OptionsParser.h"
#include "td/utils/Random.h"
#include "td/utils/filesystem.h"
#include "td/utils/tests.h"
#include "td/utils/optional.h"
#include "td/utils/overloaded.h"
#include "td/utils/MpscPollableQueue.h"
#include "td/utils/port/signals.h"
using namespace tonlib;
auto sync_send = [](auto& client, auto query) {
using ReturnTypePtr = typename std::decay_t<decltype(*query)>::ReturnType;
using ReturnType = typename ReturnTypePtr::element_type;
client.send({1, std::move(query)});
while (true) {
auto response = client.receive(100);
if (response.object) {
CHECK(response.id == 1);
if (response.object->get_id() == tonlib_api::error::ID) {
auto error = tonlib_api::move_object_as<tonlib_api::error>(response.object);
return td::Result<ReturnTypePtr>(td::Status::Error(error->code_, error->message_));
}
return td::Result<ReturnTypePtr>(tonlib_api::move_object_as<ReturnType>(response.object));
}
}
};
struct Key {
std::string public_key;
td::SecureString secret;
tonlib_api::object_ptr<tonlib_api::inputKey> get_input_key() const {
return tonlib_api::make_object<tonlib_api::inputKey>(
tonlib_api::make_object<tonlib_api::key>(public_key, secret.copy()), td::SecureString("local"));
}
};
struct Wallet {
std::string address;
Key key;
};
std::string test_giver_address(Client& client) {
using tonlib_api::make_object;
return sync_send(client, make_object<tonlib_api::testGiver_getAccountAddress>()).move_as_ok()->account_address_;
}
td::int64 get_balance(Client& client, std::string address) {
auto generic_state = sync_send(client, tonlib_api::make_object<tonlib_api::generic_getAccountState>(
tonlib_api::make_object<tonlib_api::accountAddress>(address)))
.move_as_ok();
td::int64 res = 0;
tonlib_api::downcast_call(*generic_state, [&](auto& state) { res = state.account_state_->balance_; });
return res;
}
bool is_inited(Client& client, std::string address) {
auto generic_state = sync_send(client, tonlib_api::make_object<tonlib_api::generic_getAccountState>(
tonlib_api::make_object<tonlib_api::accountAddress>(address)))
.move_as_ok();
return generic_state->get_id() != tonlib_api::generic_accountStateUninited::ID;
}
void transfer_grams(Client& client, std::string from, std::string to, td::int64 amount,
tonlib_api::object_ptr<tonlib_api::inputKey> input_key) {
auto balance = get_balance(client, to);
sync_send(client, tonlib_api::make_object<tonlib_api::generic_sendGrams>(
std::move(input_key), tonlib_api::make_object<tonlib_api::accountAddress>(from),
tonlib_api::make_object<tonlib_api::accountAddress>(to), amount))
.ensure();
while (balance == get_balance(client, to)) {
client.receive(1);
}
}
Wallet create_empty_wallet(Client& client) {
using tonlib_api::make_object;
auto key =
sync_send(client, make_object<tonlib_api::createNewKey>(td::SecureString("local"), td::SecureString("mnemonic")))
.move_as_ok();
Wallet wallet{"", {key->public_key_, std::move(key->secret_)}};
auto account_address =
sync_send(client, make_object<tonlib_api::testWallet_getAccountAddress>(
make_object<tonlib_api::testWallet_initialAccountState>(wallet.key.public_key)))
.move_as_ok();
wallet.address = account_address->account_address_;
return wallet;
}
Wallet create_wallet(Client& client) {
using tonlib_api::make_object;
auto wallet = create_empty_wallet(client);
transfer_grams(client, test_giver_address(client), wallet.address, 6000000000, {});
sync_send(client, make_object<tonlib_api::testWallet_init>(wallet.key.get_input_key())).ensure();
while (!is_inited(client, wallet.address)) {
client.receive(1);
}
LOG(ERROR) << get_balance(client, wallet.address);
return wallet;
}
std::string get_test_giver_address(Client& client) {
return sync_send(client, tonlib_api::make_object<tonlib_api::testGiver_getAccountAddress>())
.move_as_ok()
->account_address_;
}
void dump_transaction_history(Client& client, std::string address) {
using tonlib_api::make_object;
auto state = sync_send(client, make_object<tonlib_api::testGiver_getAccountState>()).move_as_ok();
auto tid = std::move(state->last_transaction_id_);
int cnt = 0;
while (tid->lt_ != 0) {
auto lt = tid->lt_;
auto got_transactions = sync_send(client, make_object<tonlib_api::raw_getTransactions>(
make_object<tonlib_api::accountAddress>(address), std::move(tid)))
.move_as_ok();
CHECK(got_transactions->transactions_.size() > 0);
CHECK(got_transactions->transactions_[0]->previous_transaction_id_->lt_ < lt);
for (auto& txn : got_transactions->transactions_) {
LOG(ERROR) << to_string(txn);
cnt++;
}
tid = std::move(got_transactions->transactions_.back()->previous_transaction_id_);
}
LOG(ERROR) << cnt;
}
int main(int argc, char* argv[]) {
td::set_default_failure_signal_handler();
using tonlib_api::make_object;
td::OptionsParser p;
std::string global_config_str;
p.add_option('C', "global-config", "file to read global config", [&](td::Slice fname) {
TRY_RESULT(str, td::read_file_str(fname.str()));
global_config_str = std::move(str);
LOG(ERROR) << global_config_str;
return td::Status::OK();
});
p.run(argc, argv).ensure();
Client client;
{
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(global_config_str, "."))).ensure();
}
dump_transaction_history(client, get_test_giver_address(client));
auto wallet_a = create_wallet(client);
auto wallet_b = create_empty_wallet(client);
transfer_grams(client, wallet_a.address, wallet_b.address, 3000000000, wallet_a.key.get_input_key());
auto a = get_balance(client, wallet_a.address);
auto b = get_balance(client, wallet_b.address);
LOG(ERROR) << a << " " << b;
return 0;
{
// init
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(global_config_str, "."))).ensure();
auto key = sync_send(client,
make_object<tonlib_api::createNewKey>(td::SecureString("local"), td::SecureString("mnemonic")))
.move_as_ok();
auto create_input_key = [&] {
return make_object<tonlib_api::inputKey>(make_object<tonlib_api::key>(key->public_key_, key->secret_.copy()),
td::SecureString("local"));
};
auto public_key_raw = key->public_key_;
td::Ed25519::PublicKey public_key_std(td::SecureString{public_key_raw});
sync_send(client, make_object<tonlib_api::options_setConfig>(global_config_str)).ensure();
auto wallet_addr = GenericAccount::get_address(0, TestWallet::get_init_state(public_key_std));
{
auto account_address =
sync_send(client, make_object<tonlib_api::testWallet_getAccountAddress>(
make_object<tonlib_api::testWallet_initialAccountState>(public_key_raw)))
.move_as_ok();
ASSERT_EQ(wallet_addr.rserialize(), account_address->account_address_);
}
std::string test_giver_address;
{
auto account_address = sync_send(client, make_object<tonlib_api::testGiver_getAccountAddress>()).move_as_ok();
test_giver_address = account_address->account_address_;
ASSERT_EQ(TestGiver::address().rserialize(), test_giver_address);
}
{
auto account_address =
sync_send(
client,
make_object<tonlib_api::raw_getAccountAddress>(make_object<tonlib_api::raw_initialAccountState>(
vm::std_boc_serialize(TestWallet::get_init_code()).move_as_ok().as_slice().str(),
vm::std_boc_serialize(TestWallet::get_init_data(public_key_std)).move_as_ok().as_slice().str())))
.move_as_ok();
ASSERT_EQ(wallet_addr.rserialize(), account_address->account_address_);
}
{
auto state = sync_send(client, make_object<tonlib_api::raw_getAccountState>(
make_object<tonlib_api::accountAddress>(wallet_addr.rserialize())))
.move_as_ok();
LOG(ERROR) << to_string(state);
}
td::int32 seqno = 0;
{
auto state = sync_send(client, make_object<tonlib_api::testGiver_getAccountState>()).move_as_ok();
LOG(ERROR) << to_string(state);
seqno = state->seqno_;
}
{
sync_send(client, make_object<tonlib_api::testGiver_sendGrams>(
make_object<tonlib_api::accountAddress>(wallet_addr.rserialize()), seqno,
1000000000ll * 6666 / 1000))
.ensure();
}
while (true) {
auto state = sync_send(client, make_object<tonlib_api::testGiver_getAccountState>()).move_as_ok();
if (state->seqno_ > seqno) {
break;
}
client.receive(1);
}
while (true) {
auto state = sync_send(client, make_object<tonlib_api::raw_getAccountState>(
make_object<tonlib_api::accountAddress>(wallet_addr.rserialize())))
.move_as_ok();
td::int64 grams_count = state->balance_;
if (grams_count > 0) {
LOG(ERROR) << "GOT " << grams_count;
break;
}
client.receive(1);
}
{ sync_send(client, make_object<tonlib_api::testWallet_init>(create_input_key())).ensure(); }
while (true) {
auto r_state = sync_send(client, make_object<tonlib_api::testWallet_getAccountState>(
make_object<tonlib_api::accountAddress>(wallet_addr.rserialize())));
if (r_state.is_ok()) {
LOG(ERROR) << to_string(r_state.ok());
break;
}
client.receive(1);
}
{
sync_send(client, make_object<tonlib_api::generic_sendGrams>(
create_input_key(), make_object<tonlib_api::accountAddress>(wallet_addr.rserialize()),
make_object<tonlib_api::accountAddress>(test_giver_address), 1000000000ll * 3333 / 1000))
.ensure();
}
while (true) {
auto generic_state = sync_send(client, make_object<tonlib_api::generic_getAccountState>(
make_object<tonlib_api::accountAddress>(wallet_addr.rserialize())))
.move_as_ok();
if (generic_state->get_id() == tonlib_api::generic_accountStateTestWallet::ID) {
auto state = tonlib_api::move_object_as<tonlib_api::generic_accountStateTestWallet>(generic_state);
if (state->account_state_->balance_ < 5617007000) {
LOG(ERROR) << to_string(state);
break;
}
}
client.receive(1);
}
{
auto generic_state = sync_send(client, make_object<tonlib_api::generic_getAccountState>(
make_object<tonlib_api::accountAddress>(test_giver_address)))
.move_as_ok();
CHECK(generic_state->get_id() == tonlib_api::generic_accountStateTestGiver::ID);
LOG(ERROR) << to_string(generic_state);
}
}
return 0;
}

158
tonlib/tonlib/Client.cpp Normal file
View file

@ -0,0 +1,158 @@
/*
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
*/
#include "Client.h"
#include "tonlib/TonlibClient.h"
#include "tonlib/TonlibCallback.h"
#include "td/actor/actor.h"
#include "td/utils/MpscPollableQueue.h"
int VERBOSITY_NAME(tonlib_requests) = VERBOSITY_NAME(DEBUG);
namespace tonlib {
class Client::Impl final {
public:
using OutputQueue = td::MpscPollableQueue<Client::Response>;
Impl() {
output_queue_ = std::make_shared<OutputQueue>();
output_queue_->init();
class Callback : public TonlibCallback {
public:
explicit Callback(std::shared_ptr<OutputQueue> output_queue) : output_queue_(std::move(output_queue)) {
}
void on_result(std::uint64_t id, tonlib_api::object_ptr<tonlib_api::Object> result) override {
output_queue_->writer_put({id, std::move(result)});
}
void on_error(std::uint64_t id, tonlib_api::object_ptr<tonlib_api::error> error) override {
output_queue_->writer_put({id, std::move(error)});
}
Callback(const Callback&) = delete;
Callback& operator=(const Callback&) = delete;
Callback(Callback&&) = delete;
Callback& operator=(Callback&&) = delete;
~Callback() override {
output_queue_->writer_put({0, nullptr});
}
private:
std::shared_ptr<OutputQueue> output_queue_;
};
scheduler_.run_in_context(
[&] { tonlib_ = td::actor::create_actor<TonlibClient>("Tonlib", td::make_unique<Callback>(output_queue_)); });
scheduler_thread_ = td::thread([&] { scheduler_.run(); });
}
void send(Client::Request request) {
if (request.id == 0 || request.function == nullptr) {
LOG(ERROR) << "Drop wrong request " << request.id;
return;
}
scheduler_.run_in_context_external(
[&] { send_closure(tonlib_, &TonlibClient::request, request.id, std::move(request.function)); });
}
Client::Response receive(double timeout) {
VLOG(tonlib_requests) << "Begin to wait for updates with timeout " << timeout;
auto is_locked = receive_lock_.exchange(true);
CHECK(!is_locked);
auto response = receive_unlocked(timeout);
is_locked = receive_lock_.exchange(false);
CHECK(is_locked);
VLOG(tonlib_requests) << "End to wait for updates, returning object " << response.id << ' '
<< response.object.get();
return response;
}
Impl(const Impl&) = delete;
Impl& operator=(const Impl&) = delete;
Impl(Impl&&) = delete;
Impl& operator=(Impl&&) = delete;
~Impl() {
LOG(ERROR) << "~Impl";
scheduler_.run_in_context_external([&] { tonlib_.reset(); });
LOG(ERROR) << "Wait till closed";
while (!is_closed_) {
receive(10);
}
LOG(ERROR) << "Stop";
scheduler_.run_in_context_external([] { td::actor::SchedulerContext::get()->stop(); });
LOG(ERROR) << "join";
scheduler_thread_.join();
}
private:
std::shared_ptr<OutputQueue> output_queue_;
int output_queue_ready_cnt_{0};
std::atomic<bool> receive_lock_{false};
bool is_closed_{false};
td::actor::Scheduler scheduler_{{0}};
td::thread scheduler_thread_;
td::actor::ActorOwn<TonlibClient> tonlib_;
Client::Response receive_unlocked(double timeout) {
if (output_queue_ready_cnt_ == 0) {
output_queue_ready_cnt_ = output_queue_->reader_wait_nonblock();
}
if (output_queue_ready_cnt_ > 0) {
output_queue_ready_cnt_--;
auto res = output_queue_->reader_get_unsafe();
if (res.object == nullptr && res.id == 0) {
is_closed_ = true;
}
return res;
}
if (timeout != 0) {
output_queue_->reader_get_event_fd().wait(static_cast<int>(timeout * 1000));
return receive_unlocked(0);
}
return {0, nullptr};
}
};
Client::Client() : impl_(std::make_unique<Impl>()) {
// At least it should be enough for everybody who uses TDLib
// FIXME
//td::init_openssl_threads();
}
void Client::send(Request&& request) {
impl_->send(std::move(request));
}
Client::Response Client::receive(double timeout) {
return impl_->receive(timeout);
}
Client::Response Client::execute(Request&& request) {
Response response;
response.id = request.id;
response.object = TonlibClient::static_request(std::move(request.function));
return response;
}
Client::~Client() = default;
Client::Client(Client&& other) = default;
Client& Client::operator=(Client&& other) = default;
} // namespace tonlib

52
tonlib/tonlib/Client.h Normal file
View file

@ -0,0 +1,52 @@
/*
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 "auto/tl/tonlib_api.h"
namespace tonlib_api = ton::tonlib_api;
namespace tonlib {
class Client final {
public:
Client();
struct Request {
std::uint64_t id;
tonlib_api::object_ptr<tonlib_api::Function> function;
};
void send(Request&& request);
struct Response {
std::uint64_t id;
tonlib_api::object_ptr<tonlib_api::Object> object;
};
Response receive(double timeout);
static Response execute(Request&& request);
~Client();
Client(Client&& other);
Client& operator=(Client&& other);
private:
class Impl;
std::unique_ptr<Impl> impl_;
};
} // namespace tonlib

View file

@ -0,0 +1,18 @@
/*
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
*/

View file

@ -0,0 +1,41 @@
/*
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 "tonlib/TonlibCallback.h"
#include "tonlib/TonlibClient.h"
#include "auto/tl/tonlib_api.h"
namespace tonlinb {
class ClientActor : public td::actor::Actor {
public:
explicit ClientActor(td::unique_ptr<TonlibCallback> callback);
void request(td::uint64 id, tonlib_api::object_ptr<tonlib_api::Function> request);
static tonlib_api::object_ptr<tonlib_api::Object> execute(tonlib_api::object_ptr<tonlib_api::Function> request);
~ClientActor();
ClientActor(ClientActor&& other);
ClientActor& operator=(ClientActor&& other);
ClientActor(const ClientActor& other) = delete;
ClientActor& operator=(const ClientActor& other) = delete;
private:
td::actor::ActorOwn<TonlibClient> tonlib_;
};
} // namespace tonlinb

View file

@ -0,0 +1,119 @@
/*
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
*/
#include "tonlib/ClientJson.h"
#include "auto/tl/tonlib_api_json.h"
#include "tl/tl_json.h"
#include "td/utils/common.h"
#include "td/utils/format.h"
#include "td/utils/JsonBuilder.h"
#include "td/utils/logging.h"
#include "td/utils/port/thread_local.h"
#include "td/utils/Status.h"
#include <utility>
namespace tonlib {
static td::Result<std::pair<tonlib_api::object_ptr<tonlib_api::Function>, std::string>> to_request(td::Slice request) {
auto request_str = request.str();
TRY_RESULT(json_value, td::json_decode(request_str));
if (json_value.type() != td::JsonValue::Type::Object) {
return td::Status::Error("Expected an Object");
}
std::string extra;
if (has_json_object_field(json_value.get_object(), "@extra")) {
extra = td::json_encode<std::string>(
get_json_object_field(json_value.get_object(), "@extra", td::JsonValue::Type::Null).move_as_ok());
}
tonlib_api::object_ptr<tonlib_api::Function> func;
TRY_STATUS(from_json(func, json_value));
return std::make_pair(std::move(func), extra);
}
static std::string from_response(const tonlib_api::Object &object, const td::string &extra) {
auto str = td::json_encode<td::string>(td::ToJson(object));
CHECK(!str.empty() && str.back() == '}');
if (!extra.empty()) {
str.pop_back();
str.reserve(str.size() + 11 + extra.size());
str += ",\"@extra\":";
str += extra;
str += '}';
}
return str;
}
static TD_THREAD_LOCAL std::string *current_output;
static td::CSlice store_string(std::string str) {
td::init_thread_local<std::string>(current_output);
*current_output = std::move(str);
return *current_output;
}
void ClientJson::send(td::Slice request) {
auto r_request = to_request(request);
if (r_request.is_error()) {
LOG(ERROR) << "Failed to parse " << tag("request", td::format::escaped(request)) << " " << r_request.error();
return;
}
std::uint64_t extra_id = extra_id_.fetch_add(1, std::memory_order_relaxed);
if (!r_request.ok_ref().second.empty()) {
std::lock_guard<std::mutex> guard(mutex_);
extra_[extra_id] = std::move(r_request.ok_ref().second);
}
client_.send(Client::Request{extra_id, std::move(r_request.ok_ref().first)});
}
td::CSlice ClientJson::receive(double timeout) {
auto response = client_.receive(timeout);
if (!response.object) {
return {};
}
std::string extra;
if (response.id != 0) {
std::lock_guard<std::mutex> guard(mutex_);
auto it = extra_.find(response.id);
if (it != extra_.end()) {
extra = std::move(it->second);
extra_.erase(it);
}
}
return store_string(from_response(*response.object, extra));
}
td::CSlice ClientJson::execute(td::Slice request) {
auto r_request = to_request(request);
if (r_request.is_error()) {
LOG(ERROR) << "Failed to parse " << tag("request", td::format::escaped(request)) << " " << r_request.error();
return {};
}
return store_string(from_response(*Client::execute(Client::Request{0, std::move(r_request.ok_ref().first)}).object,
r_request.ok().second));
}
} // namespace tonlib

View file

@ -0,0 +1,48 @@
/*
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 "tonlib/Client.h"
#include "td/utils/Slice.h"
#include <atomic>
#include <cstdint>
#include <mutex>
#include <string>
#include <unordered_map>
namespace tonlib {
class ClientJson final {
public:
void send(td::Slice request);
td::CSlice receive(double timeout);
static td::CSlice execute(td::Slice request);
private:
Client client_;
std::mutex mutex_; // for extra_
std::unordered_map<std::int64_t, std::string> extra_;
std::atomic<std::uint64_t> extra_id_{1};
};
} // namespace tonlib

70
tonlib/tonlib/Config.cpp Normal file
View file

@ -0,0 +1,70 @@
/*
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
*/
#include "Config.h"
#include "adnl/adnl-node-id.hpp"
#include "td/utils/JsonBuilder.h"
namespace tonlib {
td::Result<Config> Config::parse(std::string str) {
TRY_RESULT(json, td::json_decode(str));
if (json.type() != td::JsonValue::Type::Object) {
return td::Status::Error("Invalid config (1)");
}
//TRY_RESULT(main_type, td::get_json_object_string_field(json.get_object(), "@type", false));
//if (main_type != "config.global") {
//return td::Status::Error("Invalid config (3)");
//}
TRY_RESULT(lite_clients_obj,
td::get_json_object_field(json.get_object(), "liteservers", td::JsonValue::Type::Array, false));
auto &lite_clients = lite_clients_obj.get_array();
Config res;
for (auto &value : lite_clients) {
if (value.type() != td::JsonValue::Type::Object) {
return td::Status::Error("Invalid config (2)");
}
auto &object = value.get_object();
//TRY_RESULT(value_type, td::get_json_object_string_field(object, "@type", false));
//if (value_type != "liteclient.config.global") {
//return td::Status::Error("Invalid config (4)");
//}
TRY_RESULT(ip, td::get_json_object_int_field(object, "ip", false));
TRY_RESULT(port, td::get_json_object_int_field(object, "port", false));
Config::LiteClient client;
TRY_STATUS(client.address.init_host_port(td::IPAddress::ipv4_to_str(ip), port));
TRY_RESULT(id_obj, td::get_json_object_field(object, "id", td::JsonValue::Type::Object, false));
auto &id = id_obj.get_object();
TRY_RESULT(id_type, td::get_json_object_string_field(id, "@type", false));
if (id_type != "pub.ed25519") {
return td::Status::Error("Invalid config (5)");
}
TRY_RESULT(key_base64, td::get_json_object_string_field(id, "key", false));
TRY_RESULT(key, td::base64_decode(key_base64));
if (key.size() != 32) {
return td::Status::Error("Invalid config (6)");
}
client.adnl_id = ton::adnl::AdnlNodeIdFull(ton::pubkeys::Ed25519(td::Bits256(td::Slice(key).ubegin())));
res.lite_clients.push_back(std::move(client));
}
return res;
}
} // namespace tonlib

32
tonlib/tonlib/Config.h Normal file
View file

@ -0,0 +1,32 @@
/*
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 "adnl/adnl-node-id.hpp"
#include "td/utils/port/IPAddress.h"
namespace tonlib {
struct Config {
struct LiteClient {
ton::adnl::AdnlNodeIdFull adnl_id;
td::IPAddress address;
};
std::vector<LiteClient> lite_clients;
static td::Result<Config> parse(std::string str);
};
} // namespace tonlib

View file

@ -0,0 +1,52 @@
/*
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
*/
#include "tonlib/ExtClient.h"
#include "tonlib/LastBlock.h"
namespace tonlib {
void ExtClient::with_last_block(td::Promise<ton::BlockIdExt> promise) {
auto query_id = last_block_queries_.create(std::move(promise));
td::Promise<ton::BlockIdExt> P = [query_id, self = this,
actor_id = td::actor::actor_id()](td::Result<ton::BlockIdExt> result) {
send_lambda(actor_id, [self, query_id, result = std::move(result)]() mutable {
self->last_block_queries_.extract(query_id).set_result(std::move(result));
});
};
if (client_.last_block_actor_.empty()) {
return P.set_error(td::Status::Error(500, "No lite clients"));
}
td::actor::send_closure(client_.last_block_actor_, &LastBlock::get_last_block, std::move(P));
}
void ExtClient::send_raw_query(td::BufferSlice query, td::Promise<td::BufferSlice> promise) {
auto query_id = queries_.create(std::move(promise));
td::Promise<td::BufferSlice> P = [query_id, self = this,
actor_id = td::actor::actor_id()](td::Result<td::BufferSlice> result) {
send_lambda(actor_id, [self, query_id, result = std::move(result)]() mutable {
self->queries_.extract(query_id).set_result(std::move(result));
});
};
if (client_.andl_ext_client_.empty()) {
return P.set_error(td::Status::Error(500, "No lite clients"));
}
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));
}
} // namespace tonlib

80
tonlib/tonlib/ExtClient.h Normal file
View file

@ -0,0 +1,80 @@
/*
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 "adnl/adnl-ext-client.h"
#include "tl-utils/lite-utils.hpp"
#include "auto/tl/lite_api.h"
#include "ton/ton-types.h"
#include "td/actor/actor.h"
#include "td/utils/Container.h"
namespace tonlib {
class LastBlock;
struct ExtClientRef {
td::actor::ActorId<ton::adnl::AdnlExtClient> andl_ext_client_;
td::actor::ActorId<LastBlock> last_block_actor_;
};
class ExtClient {
public:
ExtClient() = default;
ExtClient(const ExtClient&) = delete;
ExtClient(ExtClient&&) = delete;
ExtClient& operator=(const ExtClient&) = delete;
ExtClient& operator=(ExtClient&&) = delete;
void set_client(ExtClientRef client) {
client_ = client;
}
ExtClientRef get_client() {
return client_;
}
void with_last_block(td::Promise<ton::BlockIdExt> promise);
template <class QueryT>
void send_query(QueryT query, td::Promise<typename QueryT::ReturnType> promise) {
auto raw_query = ton::serialize_tl_object(&query, true);
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));
}());
});
}
private:
ExtClientRef client_;
td::Container<td::Promise<td::BufferSlice>> queries_;
td::Container<td::Promise<ton::BlockIdExt>> last_block_queries_;
void send_raw_query(td::BufferSlice query, td::Promise<td::BufferSlice> promise);
};
} // namespace tonlib

View file

@ -0,0 +1,100 @@
/*
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
*/
#include "ExtClientLazy.h"
namespace tonlib {
class ExtClientLazyImp : public ton::adnl::AdnlExtClient {
public:
ExtClientLazyImp(ton::adnl::AdnlNodeIdFull dst, td::IPAddress dst_addr,
td::unique_ptr<ExtClientLazy::Callback> callback)
: dst_(std::move(dst)), dst_addr_(std::move(dst_addr)), callback_(std::move(callback)) {
}
void check_ready(td::Promise<td::Unit> promise) override {
before_query();
send_closure(client_, &ton::adnl::AdnlExtClient::check_ready, std::move(promise));
}
void send_query(std::string name, td::BufferSlice data, td::Timestamp timeout,
td::Promise<td::BufferSlice> promise) override {
before_query();
send_closure(client_, &ton::adnl::AdnlExtClient::send_query, std::move(name), std::move(data), timeout,
std::move(promise));
}
void before_query() {
if (is_closing_) {
return;
}
if (!client_.empty()) {
alarm_timestamp() = td::Timestamp::in(MAX_NO_QUERIES_TIMEOUT);
return;
}
class Callback : public ton::adnl::AdnlExtClient::Callback {
public:
explicit Callback(td::actor::ActorShared<> parent) : parent_(std::move(parent)) {
}
void on_ready() override {
}
void on_stop_ready() override {
}
private:
td::actor::ActorShared<> parent_;
};
ref_cnt_++;
client_ = ton::adnl::AdnlExtClient::create(dst_, dst_addr_, std::make_unique<Callback>(td::actor::actor_shared()));
}
private:
ton::adnl::AdnlNodeIdFull dst_;
td::IPAddress dst_addr_;
td::actor::ActorOwn<ton::adnl::AdnlExtClient> client_;
td::unique_ptr<ExtClientLazy::Callback> callback_;
static constexpr double MAX_NO_QUERIES_TIMEOUT = 100;
bool is_closing_{false};
td::uint32 ref_cnt_{1};
void alarm() override {
client_.reset();
}
void hangup_shared() override {
ref_cnt_--;
try_stop();
}
void hangup() override {
is_closing_ = true;
ref_cnt_--;
client_.reset();
try_stop();
}
void try_stop() {
if (is_closing_ && ref_cnt_ == 0) {
stop();
}
}
};
td::actor::ActorOwn<ton::adnl::AdnlExtClient> ExtClientLazy::create(ton::adnl::AdnlNodeIdFull dst,
td::IPAddress dst_addr,
td::unique_ptr<Callback> callback) {
return td::actor::create_actor<ExtClientLazyImp>("ExtClientLazy", dst, dst_addr, std::move(callback));
}
} // namespace tonlib

View file

@ -0,0 +1,36 @@
/*
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/actor/actor.h"
#include "adnl/adnl-ext-client.h"
namespace tonlib {
class ExtClientLazy {
public:
class Callback {
public:
virtual ~Callback() {
}
};
static td::actor::ActorOwn<ton::adnl::AdnlExtClient> create(ton::adnl::AdnlNodeIdFull dst, td::IPAddress dst_addr,
td::unique_ptr<Callback> callback);
};
} // namespace tonlib

View file

@ -0,0 +1,79 @@
/*
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
*/
#include "tonlib/GenericAccount.h"
#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) {
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) {
return block::StdAddress(workchain_id, init_state->get_hash().bits());
}
td::Ref<vm::Cell> GenericAccount::create_ext_message(const block::StdAddress& address, td::Ref<vm::Cell> new_state,
td::Ref<vm::Cell> body) {
block::gen::Message::Record message;
/*info*/ {
block::gen::CommonMsgInfo::Record_ext_in_msg_info info;
/* src */
tlb::csr_pack(info.src, block::gen::MsgAddressExt::Record_addr_none{});
/* dest */ {
block::gen::MsgAddressInt::Record_addr_std dest;
dest.anycast = vm::CellBuilder().store_zeroes(1).as_cellslice_ref();
dest.workchain_id = address.workchain;
dest.address = address.addr;
tlb::csr_pack(info.dest, dest);
}
/* import_fee */ {
vm::CellBuilder cb;
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(0));
info.import_fee = cb.as_cellslice_ref();
}
tlb::csr_pack(message.info, info);
}
/* init */ {
if (new_state.not_null()) {
// Just(Left(new_state))
message.init = vm::CellBuilder()
.store_ones(1)
.store_zeroes(1)
.append_cellslice(vm::load_cell_slice(new_state))
.as_cellslice_ref();
} else {
message.init = vm::CellBuilder().store_zeroes(1).as_cellslice_ref();
CHECK(message.init.not_null());
}
}
/* body */ {
message.body = vm::CellBuilder().store_zeroes(1).append_cellslice(vm::load_cell_slice_ref(body)).as_cellslice_ref();
}
td::Ref<vm::Cell> res;
tlb::type_pack_cell(res, block::gen::t_Message_Any, message);
CHECK(res.not_null());
return res;
}
} // namespace tonlib

View file

@ -0,0 +1,30 @@
/*
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 "vm/cells.h"
#include "block/block.h"
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> create_ext_message(const block::StdAddress& address, td::Ref<vm::Cell> new_state,
td::Ref<vm::Cell> body);
};
} // namespace tonlib

View file

@ -0,0 +1,148 @@
/*
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
*/
#include "KeyStorage.h"
#include "tonlib/keys/Mnemonic.h"
#include "tonlib/keys/DecryptedKey.h"
#include "tonlib/keys/EncryptedKey.h"
#include "td/utils/filesystem.h"
#include "td/utils/port/path.h"
namespace tonlib {
std::string to_file_name(td::Slice public_key) {
return td::buffer_to_hex(public_key);
}
std::string KeyStorage::to_file_path(td::Slice public_key) {
return directory_ + TD_DIR_SLASH + to_file_name(public_key);
}
td::Status KeyStorage::set_directory(std::string directory) {
TRY_RESULT(path, td::realpath(directory));
TRY_RESULT(stat, td::stat(path));
if (!stat.is_dir_) {
return td::Status::Error("not a directory");
}
directory_ = std::move(path);
return td::Status::OK();
}
td::Result<KeyStorage::Key> KeyStorage::save_key(const DecryptedKey &decrypted_key, td::Slice local_password) {
auto encrypted_key = decrypted_key.encrypt(local_password);
Key res;
res.public_key = encrypted_key.public_key.as_octet_string();
res.secret = std::move(encrypted_key.secret);
auto size = encrypted_key.encrypted_data.size();
LOG(ERROR) << "SAVE " << to_file_name(res.public_key);
TRY_RESULT(to_file, td::FileFd::open(to_file_path(res.public_key), td::FileFd::CreateNew | td::FileFd::Write));
TRY_RESULT(written, to_file.write(encrypted_key.encrypted_data));
if (written != static_cast<size_t>(size)) {
return td::Status::Error(PSLICE() << "Failed to write file: written " << written << " bytes instead of " << size);
}
to_file.close();
return std::move(res);
}
td::Result<KeyStorage::Key> KeyStorage::create_new_key(td::Slice local_password, td::Slice mnemonic_password) {
Mnemonic::Options create_options;
create_options.password = td::SecureString(mnemonic_password);
TRY_RESULT(mnemonic, Mnemonic::create_new(std::move(create_options)));
return save_key(DecryptedKey(std::move(mnemonic)), local_password);
}
td::Result<DecryptedKey> KeyStorage::export_decrypted_key(InputKey input_key) {
TRY_RESULT(encrypted_data, td::read_file_secure(to_file_path(input_key.key.public_key)));
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));
}
td::Result<KeyStorage::ExportedKey> KeyStorage::export_key(InputKey input_key) {
TRY_RESULT(decrypted_key, export_decrypted_key(std::move(input_key)));
ExportedKey exported_key;
exported_key.mnemonic_words = std::move(decrypted_key.mnemonic_words);
return std::move(exported_key);
}
td::Result<KeyStorage::PrivateKey> KeyStorage::load_private_key(InputKey input_key) {
TRY_RESULT(decrypted_key, export_decrypted_key(std::move(input_key)));
PrivateKey private_key;
private_key.private_key = decrypted_key.private_key.as_octet_string();
return std::move(private_key);
}
td::Status KeyStorage::delete_key(td::Slice public_key) {
return td::unlink(to_file_path(public_key));
}
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 td::Status::Error("Invalid mnemonic words or password (invalid checksum)");
}
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));
return ExportedPemKey{std::move(pem)};
}
td::Result<KeyStorage::Key> KeyStorage::change_local_password(InputKey input_key, td::Slice new_local_password) {
auto new_secret =
DecryptedKey::change_local_password(input_key.key.secret, input_key.local_password, new_local_password);
Key res;
res.public_key = std::move(input_key.key.public_key);
res.secret = std::move(new_secret);
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));
return save_key(DecryptedKey({}, std::move(key)), local_password);
}
static std::string dummy_secret = "dummy secret of 32 bytes length!";
td::Result<KeyStorage::ExportedEncryptedKey> KeyStorage::export_encrypted_key(InputKey input_key,
td::Slice key_password) {
TRY_RESULT(decrypted_key, export_decrypted_key(std::move(input_key)));
auto res = decrypted_key.encrypt(key_password, dummy_secret);
return ExportedEncryptedKey{std::move(res.encrypted_data)};
}
td::Result<KeyStorage::Key> KeyStorage::import_encrypted_key(td::Slice local_password, td::Slice key_password,
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));
return save_key(std::move(decrypted_key), local_password);
}
} // namespace tonlib

View file

@ -0,0 +1,77 @@
/*
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 "td/utils/SharedSlice.h"
#include <string>
namespace tonlib {
struct DecryptedKey;
class KeyStorage {
public:
struct Key {
td::SecureString public_key;
td::SecureString secret;
};
struct InputKey {
Key key;
td::SecureString local_password;
};
struct ExportedKey {
std::vector<td::SecureString> mnemonic_words;
};
struct ExportedPemKey {
td::SecureString pem;
};
struct ExportedEncryptedKey {
td::SecureString data;
};
struct PrivateKey {
td::SecureString private_key;
};
td::Status set_directory(std::string directory);
td::Result<Key> create_new_key(td::Slice local_password, td::Slice key_password);
td::Result<ExportedKey> export_key(InputKey input_key);
td::Result<ExportedPemKey> export_pem_key(InputKey input_key, td::Slice key_password);
td::Result<ExportedEncryptedKey> export_encrypted_key(InputKey input_key, td::Slice key_password);
td::Result<Key> change_local_password(InputKey input_key, td::Slice new_local_password);
td::Status delete_key(td::Slice public_key);
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);
td::Result<Key> import_encrypted_key(td::Slice local_password, td::Slice key_password,
ExportedEncryptedKey exported_key);
td::Result<PrivateKey> load_private_key(InputKey input_key);
private:
std::string directory_;
td::Result<Key> save_key(const DecryptedKey& mnemonic, td::Slice local_password);
td::Result<DecryptedKey> export_decrypted_key(InputKey input_key);
std::string to_file_path(td::Slice public_key);
};
} // namespace tonlib

View file

@ -0,0 +1,86 @@
/*
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
*/
#include "tonlib/LastBlock.h"
#include "ton/lite-tl.hpp"
namespace tonlib {
LastBlock::LastBlock(ExtClientRef client, td::actor::ActorShared<> parent) {
client_.set_client(client);
parent_ = std::move(parent);
}
void LastBlock::get_last_block(td::Promise<ton::BlockIdExt> promise) {
if (promises_.empty()) {
do_get_last_block();
}
promises_.push_back(std::move(promise));
}
void LastBlock::do_get_last_block() {
client_.send_query(ton::lite_api::liteServer_getMasterchainInfo(),
[this](auto r_info) { this->on_masterchain_info(std::move(r_info)); });
}
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_));
}
for (auto& promise : promises_) {
auto copy = mc_last_block_id_;
promise.set_value(std::move(copy));
}
promises_.clear();
}
void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id) {
if (!zero_state_id.is_valid()) {
LOG(ERROR) << "Ignore invalid zero state update";
return;
}
if (!zero_state_id_.is_valid()) {
LOG(INFO) << "Init zerostate: " << zero_state_id.to_str();
zero_state_id_ = std::move(zero_state_id);
return;
}
if (zero_state_id_ == zero_state_id_) {
return;
}
LOG(FATAL) << "Masterchain zerostate mismatch: expected: " << zero_state_id_.to_str() << ", found "
<< zero_state_id.to_str();
// TODO: all other updates will be inconsitent.
// One will have to restart ton client
}
void LastBlock::update_mc_last_block(ton::BlockIdExt mc_block_id) {
if (!mc_block_id.is_valid()) {
LOG(ERROR) << "Ignore invalid masterchain block";
return;
}
if (!mc_last_block_id_.is_valid() || mc_last_block_id_.id.seqno < mc_block_id.id.seqno) {
mc_last_block_id_ = mc_block_id;
LOG(INFO) << "Update masterchain block id: " << mc_last_block_id_.to_str();
}
}
} // namespace tonlib

47
tonlib/tonlib/LastBlock.h Normal file
View file

@ -0,0 +1,47 @@
/*
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/actor/actor.h"
#include "tonlib/ExtClient.h"
namespace tonlib {
class LastBlock : public td::actor::Actor {
public:
explicit LastBlock(ExtClientRef client, td::actor::ActorShared<> parent);
void get_last_block(td::Promise<ton::BlockIdExt> promise);
private:
ExtClient client_;
ton::ZeroStateIdExt zero_state_id_;
ton::BlockIdExt mc_last_block_id_;
std::vector<td::Promise<ton::BlockIdExt>> promises_;
td::actor::ActorShared<> parent_;
void do_get_last_block();
void on_masterchain_info(td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_masterchainInfo>> r_info);
void update_zero_state(ton::ZeroStateIdExt zero_state_id);
void update_mc_last_block(ton::BlockIdExt mc_block_id);
};
} // namespace tonlib

View file

@ -0,0 +1,50 @@
/*
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
*/
#include "tonlib/TestGiver.h"
#include "tonlib/utils.h"
#include "td/utils/base64.h"
namespace tonlib {
const block::StdAddress& TestGiver::address() {
static block::StdAddress res =
block::StdAddress::parse("-1:8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d").move_as_ok();
return res;
}
vm::CellHash TestGiver::get_init_code_hash() {
return vm::CellHash::from_slice(td::base64_decode("s7RouN9wfJ4Avx8h0uw6X3ZEJfN3MYOUmzrC8JXfMAw=").move_as_ok());
}
td::Ref<vm::Cell> TestGiver::make_a_gift_message(td::uint32 seqno, td::uint64 gramms,
const block::StdAddress& dest_address) {
td::BigInt256 dest_addr;
dest_addr.import_bits(dest_address.addr.as_bitslice());
vm::CellBuilder cb;
cb.append_cellslice(binary_bitstring_to_cellslice("b{01}").move_as_ok())
.store_long(dest_address.bounceable, 1)
.append_cellslice(binary_bitstring_to_cellslice("b{000100}").move_as_ok())
.store_long(dest_address.workchain, 8)
.store_int256(dest_addr, 256);
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms));
auto message_inner = cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("GIFT").finalize();
return vm::CellBuilder().store_long(seqno, 32).store_long(1, 8).store_ref(message_inner).finalize();
}
} // namespace tonlib

29
tonlib/tonlib/TestGiver.h Normal file
View file

@ -0,0 +1,29 @@
/*
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 "block/block.h"
namespace tonlib {
class TestGiver {
public:
static const block::StdAddress& address();
static vm::CellHash get_init_code_hash();
static td::Ref<vm::Cell> make_a_gift_message(td::uint32 seqno, td::uint64 gramms,
const block::StdAddress& dest_address);
};
} // namespace tonlib

View file

@ -0,0 +1,74 @@
/*
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
*/
#include "tonlib/TestWallet.h"
#include "tonlib/GenericAccount.h"
#include "tonlib/utils.h"
#include "vm/boc.h"
#include "td/utils/base64.h"
namespace tonlib {
td::Ref<vm::Cell> TestWallet::get_init_state(const td::Ed25519::PublicKey& public_key) {
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) {
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();
return vm::CellBuilder().store_bytes(signature).store_bytes(seq_no).finalize();
}
td::Ref<vm::Cell> TestWallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno,
td::int64 gramms, const block::StdAddress& dest_address) {
td::BigInt256 dest_addr;
dest_addr.import_bits(dest_address.addr.as_bitslice());
vm::CellBuilder cb;
cb.append_cellslice(binary_bitstring_to_cellslice("b{010000100}").move_as_ok())
.store_long(dest_address.workchain, 8)
.store_int256(dest_addr, 256);
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms));
auto message_inner = cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("GIFT").finalize();
auto message = vm::CellBuilder().store_long(seqno, 32).store_long(1, 8).store_ref(message_inner).finalize();
std::string seq_no(4, 0);
auto signature = private_key.sign(message->get_hash().as_slice()).move_as_ok();
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message)).finalize();
}
td::Ref<vm::Cell> TestWallet::get_init_code() {
static auto res = [] {
auto serialized_code = td::base64_decode(
"te6ccgEEAQEAAAAARAAAhP8AIN2k8mCBAgDXGCDXCx/tRNDTH9P/"
"0VESuvKhIvkBVBBE+RDyovgAAdMfMSDXSpbTB9QC+wDe0aTIyx/L/8ntVA==")
.move_as_ok();
return vm::std_boc_deserialize(serialized_code).move_as_ok();
}();
return res;
}
vm::CellHash TestWallet::get_init_code_hash() {
return get_init_code()->get_hash();
}
td::Ref<vm::Cell> TestWallet::get_init_data(const td::Ed25519::PublicKey& public_key) {
return vm::CellBuilder().store_long(0, 32).store_bytes(public_key.as_octet_string()).finalize();
}
} // namespace tonlib

View file

@ -0,0 +1,37 @@
/*
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 "vm/cells.h"
#include "Ed25519.h"
#include "block/block.h"
namespace tonlib {
class TestWallet {
public:
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> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno,
td::int64 gramms, const block::StdAddress& dest_address);
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);
};
} // namespace tonlib

View file

@ -0,0 +1,30 @@
/*
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 "auto/tl/tonlib_api.h"
namespace tonlib_api = ton::tonlib_api;
class TonlibCallback {
public:
virtual void on_result(std::uint64_t id, tonlib_api::object_ptr<tonlib_api::Object> result) = 0;
virtual void on_error(std::uint64_t id, tonlib_api::object_ptr<tonlib_api::error> error) = 0;
virtual ~TonlibCallback() = default;
};

View file

@ -0,0 +1,996 @@
/*
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
*/
#include "TonlibClient.h"
#include "tonlib/ExtClientLazy.h"
#include "tonlib/GenericAccount.h"
#include "tonlib/LastBlock.h"
#include "tonlib/TestWallet.h"
#include "tonlib/TestGiver.h"
#include "tonlib/utils.h"
#include "auto/tl/tonlib_api.hpp"
#include "block/block-auto.h"
#include "block/check-proof.h"
#include "ton/lite-tl.hpp"
#include "ton/ton-shard.h"
#include "vm/boc.h"
#include "td/utils/Random.h"
#include "td/utils/overloaded.h"
#include "td/utils/tests.h"
#include "td/utils/port/path.h"
namespace ton {} // namespace ton
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; })
static block::AccountState create_account_state(ton::tl_object_ptr<ton::lite_api::liteServer_accountState> from) {
block::AccountState res;
res.blk = ton::create_block_id(from->id_);
res.shard_blk = ton::create_block_id(from->shardblk_);
res.shard_proof = std::move(from->shard_proof_);
res.proof = std::move(from->proof_);
res.state = std::move(from->state_);
return res;
}
struct RawAccountState {
td::int64 balance = -1;
td::Ref<vm::CellSlice> code;
td::Ref<vm::CellSlice> data;
block::AccountState::Info info;
};
td::Result<td::int64> to_balance_or_throw(td::Ref<vm::CellSlice> balance_ref) {
vm::CellSlice balance_slice = *balance_ref;
auto balance = block::tlb::t_Grams.as_integer_skip(balance_slice);
if (balance.is_null()) {
return td::Status::Error("Failed to unpack balance");
}
auto res = balance->to_long();
if (res == td::int64(~0ULL << 63)) {
return td::Status::Error("Failed to unpack balance (2)");
}
return res;
}
td::Result<td::int64> to_balance(td::Ref<vm::CellSlice> balance_ref) {
return TRY_VM(to_balance_or_throw(std::move(balance_ref)));
}
class GetTransactionHistory : public td::actor::Actor {
public:
GetTransactionHistory(ExtClientRef ext_client_ref, block::StdAddress address, ton::LogicalTime lt, ton::Bits256 hash,
td::Promise<block::TransactionList::Info> promise)
: address_(std::move(address)), lt_(std::move(lt)), hash_(std::move(hash)), promise_(std::move(promise)) {
client_.set_client(ext_client_ref);
}
private:
block::StdAddress address_;
ton::LogicalTime lt_;
ton::Bits256 hash_;
ExtClient client_;
td::int32 count_{10};
td::Promise<block::TransactionList::Info> promise_;
void check(td::Status status) {
if (status.is_error()) {
LOG(ERROR) << status;
promise_.set_error(std::move(status));
stop();
}
}
td::Status do_with_transactions(std::vector<ton::BlockIdExt> blkids, td::BufferSlice transactions) {
LOG(INFO) << "got up to " << count_ << " transactions for " << address_ << " from last transaction " << lt_ << ":"
<< hash_.to_hex();
block::TransactionList list;
list.blkids = std::move(blkids);
list.hash = hash_;
list.lt = lt_;
list.transactions_boc = std::move(transactions);
TRY_RESULT(info, list.validate());
if (info.transactions.size() > static_cast<size_t>(count_)) {
LOG(WARNING) << "obtained " << info.transactions.size() << " transaction, but only " << count_
<< " have been requested";
}
promise_.set_value(std::move(info));
return td::Status::OK();
}
td::Status do_with_transactions(
td::Result<ton::lite_api::object_ptr<ton::lite_api::liteServer_transactionList>> r_transactions) {
TRY_RESULT(transactions, std::move(r_transactions));
std::vector<ton::BlockIdExt> blkids;
for (auto& id : transactions->ids_) {
blkids.push_back(ton::create_block_id(std::move(id)));
}
return do_with_transactions(std::move(blkids), std::move(transactions->transactions_));
}
void with_transactions(
td::Result<ton::lite_api::object_ptr<ton::lite_api::liteServer_transactionList>> r_transactions) {
check(TRY_VM(do_with_transactions(std::move(r_transactions))));
stop();
}
void start_up() override {
client_.send_query(
ton::lite_api::liteServer_getTransactions(
count_, ton::create_tl_object<ton::lite_api::liteServer_accountId>(address_.workchain, address_.addr), lt_,
hash_),
[self = this](auto r_transactions) { self->with_transactions(std::move(r_transactions)); });
}
};
class GetRawAccountState : public td::actor::Actor {
public:
GetRawAccountState(ExtClientRef ext_client_ref, block::StdAddress address, td::Promise<RawAccountState>&& promise)
: address_(std::move(address)), promise_(std::move(promise)) {
client_.set_client(ext_client_ref);
}
private:
block::StdAddress address_;
td::Promise<RawAccountState> promise_;
ExtClient client_;
ton::BlockIdExt last_block_;
void with_account_state(td::Result<ton::tl_object_ptr<ton::lite_api::liteServer_accountState>> r_account_state) {
promise_.set_result(TRY_VM(do_with_account_state(std::move(r_account_state))));
stop();
}
td::Result<RawAccountState> do_with_account_state(
td::Result<ton::tl_object_ptr<ton::lite_api::liteServer_accountState>> r_account_state) {
TRY_RESULT(raw_account_state, std::move(r_account_state));
auto account_state = create_account_state(std::move(raw_account_state));
TRY_RESULT(info, account_state.validate(last_block_, address_));
auto serialized_state = account_state.state.clone();
LOG(ERROR) << serialized_state.size();
RawAccountState res;
res.info = std::move(info);
auto cell = res.info.root;
if (cell.is_null()) {
return res;
}
//block::gen::t_Account.print_ref(std::cerr, cell);
block::gen::Account::Record_account account;
if (!tlb::unpack_cell(cell, account)) {
return td::Status::Error("Failed to unpack Account");
}
block::gen::AccountStorage::Record storage;
if (!tlb::csr_unpack(account.storage, storage)) {
return td::Status::Error("Failed to unpack AccountStorage");
}
TRY_RESULT(balance, to_balance(storage.balance));
res.balance = balance;
auto state_tag = block::gen::t_AccountState.get_tag(*storage.state);
if (state_tag < 0) {
return td::Status::Error("Failed to parse AccountState tag");
}
// TODO: handle frozen account
if (state_tag != block::gen::AccountState::account_active) {
return res;
}
block::gen::AccountState::Record_account_active state;
if (!tlb::csr_unpack(storage.state, state)) {
return td::Status::Error("Failed to parse AccountState");
}
block::gen::StateInit::Record state_init;
if (!tlb::csr_unpack(state.x, state_init)) {
return td::Status::Error("Failed to parse StateInit");
}
res.code = std::move(state_init.code);
res.data = std::move(state_init.data);
return res;
}
void start_up() override {
client_.with_last_block([self = this](td::Result<ton::BlockIdExt> r_last_block) {
if (r_last_block.is_error()) {
return self->check(r_last_block.move_as_error());
}
self->last_block_ = r_last_block.move_as_ok();
self->client_.send_query(
ton::lite_api::liteServer_getAccountState(ton::create_tl_lite_block_id(self->last_block_),
ton::create_tl_object<ton::lite_api::liteServer_accountId>(
self->address_.workchain, self->address_.addr)),
[self](auto r_state) { self->with_account_state(std::move(r_state)); });
});
}
void check(td::Status status) {
if (status.is_error()) {
promise_.set_error(std::move(status));
stop();
}
}
};
TonlibClient::TonlibClient(td::unique_ptr<TonlibCallback> callback) : callback_(std::move(callback)) {
}
TonlibClient::~TonlibClient() = default;
void TonlibClient::hangup() {
is_closing_ = true;
ref_cnt_--;
raw_client_ = {};
raw_last_block_ = {};
try_stop();
}
ExtClientRef TonlibClient::get_client_ref() {
ExtClientRef ref;
ref.andl_ext_client_ = raw_client_.get();
ref.last_block_actor_ = raw_last_block_.get();
return ref;
}
void TonlibClient::init_ext_client() {
auto lite_clients_size = config_.lite_clients.size();
CHECK(lite_clients_size != 0);
auto lite_client_id = td::Random::fast(0, td::narrow_cast<int>(lite_clients_size) - 1);
auto& lite_client = config_.lite_clients[lite_client_id];
class Callback : public ExtClientLazy::Callback {
public:
explicit Callback(td::actor::ActorShared<> parent) : parent_(std::move(parent)) {
}
private:
td::actor::ActorShared<> parent_;
};
ref_cnt_++;
raw_client_ = ExtClientLazy::create(lite_client.adnl_id, lite_client.address,
td::make_unique<Callback>(td::actor::actor_shared()));
ref_cnt_++;
raw_last_block_ = td::actor::create_actor<LastBlock>("LastBlock", get_client_ref(), td::actor::actor_shared());
client_.set_client(get_client_ref());
}
void TonlibClient::on_result(td::uint64 id, tonlib_api::object_ptr<tonlib_api::Object> response) {
if (response->get_id() == tonlib_api::error::ID) {
callback_->on_error(id, tonlib_api::move_object_as<tonlib_api::error>(response));
return;
}
callback_->on_result(id, std::move(response));
}
void TonlibClient::request(td::uint64 id, tonlib_api::object_ptr<tonlib_api::Function> function) {
if (function == nullptr) {
LOG(ERROR) << "Receive empty static request";
return on_result(id, tonlib_api::make_object<tonlib_api::error>(400, "Request is empty"));
}
if (is_static_request(function->get_id())) {
return on_result(id, static_request(std::move(function)));
}
if (state_ == State::Closed) {
return on_result(id, tonlib_api::make_object<tonlib_api::error>(400, "tonlib is closed"));
}
if (state_ == State::Uninited) {
if (!is_uninited_request(function->get_id())) {
return on_result(id, tonlib_api::make_object<tonlib_api::error>(400, "library is not inited"));
}
}
downcast_call(*function, [this, self = this, id](auto& request) {
using ReturnType = typename std::decay_t<decltype(request)>::ReturnType;
td::Promise<ReturnType> promise = [actor_id = actor_id(self), id](td::Result<ReturnType> r_result) {
tonlib_api::object_ptr<tonlib_api::Object> result;
if (r_result.is_error()) {
result = tonlib_api::make_object<tonlib_api::error>(r_result.error().code(), r_result.error().message().str());
} else {
result = r_result.move_as_ok();
}
send_closure(actor_id, &TonlibClient::on_result, id, std::move(result));
};
auto status = this->do_request(request, std::move(promise));
if (status.is_error()) {
CHECK(promise);
promise.set_error(std::move(status));
}
});
}
void TonlibClient::close() {
stop();
}
tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::static_request(
tonlib_api::object_ptr<tonlib_api::Function> function) {
if (function == nullptr) {
LOG(ERROR) << "Receive empty static request";
return tonlib_api::make_object<tonlib_api::error>(400, "Request is empty");
}
tonlib_api::object_ptr<tonlib_api::Object> response;
downcast_call(*function, [&response](auto& request) { response = TonlibClient::do_static_request(request); });
return response;
}
bool TonlibClient::is_static_request(td::int32 id) {
switch (id) {
case tonlib_api::runTests::ID:
return true;
default:
return false;
}
}
bool TonlibClient::is_uninited_request(td::int32 id) {
switch (id) {
case tonlib_api::init::ID:
case tonlib_api::close::ID:
return true;
default:
return false;
}
}
tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(const tonlib_api::runTests& request) {
auto& runner = td::TestsRunner::get_default();
if (!request.dir_.empty()) {
td::chdir(request.dir_).ignore();
}
runner.run_all();
return tonlib_api::make_object<tonlib_api::ok>();
}
td::Status TonlibClient::do_request(const tonlib_api::init& request,
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
if (state_ != State::Uninited) {
return td::Status::Error(400, "Tonlib is already inited");
}
if (!request.options_) {
return td::Status::Error(400, "Field options must not be empty");
}
TRY_STATUS(key_storage_.set_directory(request.options_->keystore_directory_));
if (!request.options_->config_.empty()) {
TRY_STATUS(set_config(std::move(request.options_->config_)));
}
state_ = State::Running;
promise.set_value(tonlib_api::make_object<tonlib_api::ok>());
return td::Status::OK();
}
td::Status TonlibClient::set_config(std::string config) {
if (config.empty()) {
return td::Status::Error("config is empty");
}
TRY_RESULT(new_config, Config::parse(std::move(config)));
if (new_config.lite_clients.empty()) {
return td::Status::Error("No lite clients in config");
}
config_ = std::move(new_config);
init_ext_client();
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::close& request,
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
CHECK(state_ != State::Closed);
state_ = State::Closed;
promise.set_value(tonlib_api::make_object<tonlib_api::ok>());
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::options_setConfig& request,
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
TRY_STATUS(set_config(request.config_));
promise.set_value(tonlib_api::make_object<tonlib_api::ok>());
return td::Status::OK();
}
td::Result<block::StdAddress> get_account_address(const tonlib_api::raw_initialAccountState& raw_state) {
TRY_RESULT(code, vm::std_boc_deserialize(raw_state.code_));
TRY_RESULT(data, vm::std_boc_deserialize(raw_state.data_));
return GenericAccount::get_address(0 /*zerochain*/, GenericAccount::get_init_state(std::move(code), std::move(data)));
}
td::Result<block::StdAddress> get_account_address(const tonlib_api::testWallet_initialAccountState& test_wallet_state) {
auto key = td::Ed25519::PublicKey(td::SecureString(test_wallet_state.public_key_));
return GenericAccount::get_address(0 /*zerochain*/, TestWallet::get_init_state(key));
}
tonlib_api::object_ptr<tonlib_api::internal_transactionId> to_transaction_id(const block::AccountState::Info& info) {
return tonlib_api::make_object<tonlib_api::internal_transactionId>(info.last_trans_lt,
info.last_trans_hash.as_slice().str());
}
td::Result<tonlib_api::object_ptr<tonlib_api::raw_accountState>> to_raw_accountState(RawAccountState&& raw_state) {
std::string code;
if (raw_state.code.not_null()) {
code = vm::std_boc_serialize(vm::CellBuilder().append_cellslice(std::move(raw_state.code)).finalize())
.move_as_ok()
.as_slice()
.str();
}
std::string data;
if (raw_state.data.not_null()) {
data = vm::std_boc_serialize(vm::CellBuilder().append_cellslice(std::move(raw_state.data)).finalize())
.move_as_ok()
.as_slice()
.str();
}
return tonlib_api::make_object<tonlib_api::raw_accountState>(raw_state.balance, std::move(code), std::move(data),
to_transaction_id(raw_state.info));
}
td::Result<std::string> to_std_address_or_throw(td::Ref<vm::CellSlice> cs) {
auto tag = block::gen::MsgAddressInt().get_tag(*cs);
if (tag < 0) {
return td::Status::Error("Failed to read MsgAddressInt tag");
}
if (tag != block::gen::MsgAddressInt::addr_std) {
return "";
}
block::gen::MsgAddressInt::Record_addr_std addr;
if (!tlb::csr_unpack(cs, addr)) {
return td::Status::Error("Failed to unpack MsgAddressInt");
}
return block::StdAddress(addr.workchain_id, addr.address).rserialize();
}
td::Result<std::string> to_std_address(td::Ref<vm::CellSlice> cs) {
return TRY_VM(to_std_address_or_throw(std::move(cs)));
}
td::Result<tonlib_api::object_ptr<tonlib_api::raw_message>> to_raw_message_or_throw(td::Ref<vm::Cell> cell) {
block::gen::Message::Record message;
if (!tlb::type_unpack_cell(cell, block::gen::t_Message_Any, message)) {
return td::Status::Error("Failed to unpack Message");
}
auto tag = block::gen::CommonMsgInfo().get_tag(*message.info);
if (tag < 0) {
return td::Status::Error("Failed to read CommonMsgInfo tag");
}
switch (tag) {
case block::gen::CommonMsgInfo::int_msg_info: {
block::gen::CommonMsgInfo::Record_int_msg_info msg_info;
if (!tlb::csr_unpack(message.info, msg_info)) {
return td::Status::Error("Failed to unpack CommonMsgInfo::int_msg_info");
}
TRY_RESULT(balance, to_balance(msg_info.value));
TRY_RESULT(src, to_std_address(msg_info.src));
TRY_RESULT(dest, to_std_address(msg_info.dest));
return tonlib_api::make_object<tonlib_api::raw_message>(std::move(src), std::move(dest), balance);
}
case block::gen::CommonMsgInfo::ext_in_msg_info: {
block::gen::CommonMsgInfo::Record_ext_in_msg_info msg_info;
if (!tlb::csr_unpack(message.info, msg_info)) {
return td::Status::Error("Failed to unpack CommonMsgInfo::ext_in_msg_info");
}
TRY_RESULT(dest, to_std_address(msg_info.dest));
return tonlib_api::make_object<tonlib_api::raw_message>("", std::move(dest), 0);
}
case block::gen::CommonMsgInfo::ext_out_msg_info: {
block::gen::CommonMsgInfo::Record_ext_out_msg_info msg_info;
if (!tlb::csr_unpack(message.info, msg_info)) {
return td::Status::Error("Failed to unpack CommonMsgInfo::ext_out_msg_info");
}
TRY_RESULT(src, to_std_address(msg_info.src));
return tonlib_api::make_object<tonlib_api::raw_message>(std::move(src), "", 0);
}
}
return td::Status::Error("Unknown CommonMsgInfo tag");
}
td::Result<tonlib_api::object_ptr<tonlib_api::raw_message>> to_raw_message(td::Ref<vm::Cell> cell) {
return TRY_VM(to_raw_message_or_throw(std::move(cell)));
}
td::Result<tonlib_api::object_ptr<tonlib_api::raw_transaction>> to_raw_transaction_or_throw(
block::Transaction::Info&& info) {
std::string data;
tonlib_api::object_ptr<tonlib_api::raw_message> in_msg;
std::vector<tonlib_api::object_ptr<tonlib_api::raw_message>> out_msgs;
td::int64 fees = 0;
if (info.transaction.not_null()) {
TRY_RESULT(copy_data, vm::std_boc_serialize(info.transaction));
data = copy_data.as_slice().str();
block::gen::Transaction::Record trans;
if (!tlb::unpack_cell(info.transaction, trans)) {
return td::Status::Error("Failed to unpack Transaction");
}
TRY_RESULT(copy_fees, to_balance(trans.total_fees));
fees = copy_fees;
std::ostringstream outp;
block::gen::t_Transaction.print_ref(outp, info.transaction);
LOG(ERROR) << outp.str();
auto is_just = trans.r1.in_msg->prefetch_long(1);
if (is_just == trans.r1.in_msg->fetch_long_eof) {
return td::Status::Error("Failed to parse long");
}
if (is_just == 1) {
auto msg = trans.r1.in_msg->prefetch_ref();
TRY_RESULT(in_msg_copy, to_raw_message(trans.r1.in_msg->prefetch_ref()));
in_msg = std::move(in_msg_copy);
}
if (trans.outmsg_cnt != 0) {
vm::Dictionary dict{trans.r1.out_msgs, 15};
for (int x = 0; x < trans.outmsg_cnt && x < 100; x++) {
TRY_RESULT(out_msg, to_raw_message(dict.lookup_ref(td::BitArray<15>{x})));
out_msgs.push_back(std::move(out_msg));
}
}
}
return tonlib_api::make_object<tonlib_api::raw_transaction>(
data,
tonlib_api::make_object<tonlib_api::internal_transactionId>(info.prev_trans_lt,
info.prev_trans_hash.as_slice().str()),
fees, std::move(in_msg), std::move(out_msgs));
}
td::Result<tonlib_api::object_ptr<tonlib_api::raw_transaction>> to_raw_transaction(block::Transaction::Info&& info) {
return TRY_VM(to_raw_transaction_or_throw(std::move(info)));
}
td::Result<tonlib_api::object_ptr<tonlib_api::raw_transactions>> to_raw_transactions(
block::TransactionList::Info&& info) {
std::vector<tonlib_api::object_ptr<tonlib_api::raw_transaction>> transactions;
for (auto& transaction : info.transactions) {
TRY_RESULT(raw_transaction, to_raw_transaction(std::move(transaction)));
transactions.push_back(std::move(raw_transaction));
}
return tonlib_api::make_object<tonlib_api::raw_transactions>(std::move(transactions));
}
td::Result<tonlib_api::object_ptr<tonlib_api::testWallet_accountState>> to_testWallet_accountState(
RawAccountState&& raw_state) {
if (raw_state.data.is_null()) {
return td::Status::Error(400, "Not a TestWallet");
}
auto ref = raw_state.data->prefetch_ref();
auto cs = vm::load_cell_slice(std::move(ref));
auto seqno = cs.fetch_ulong(32);
if (seqno == cs.fetch_ulong_eof) {
return td::Status::Error("Failed to parse seq_no");
}
return tonlib_api::make_object<tonlib_api::testWallet_accountState>(raw_state.balance, static_cast<td::uint32>(seqno),
to_transaction_id(raw_state.info));
}
td::Result<tonlib_api::object_ptr<tonlib_api::testGiver_accountState>> to_testGiver_accountState(
RawAccountState&& raw_state) {
if (raw_state.data.is_null()) {
return td::Status::Error(400, "Not a TestGiver");
}
auto ref = raw_state.data->prefetch_ref();
auto cs = vm::load_cell_slice(std::move(ref));
auto seqno = cs.fetch_ulong(32);
if (seqno == cs.fetch_ulong_eof) {
return td::Status::Error("Failed to parse seq_no");
}
return tonlib_api::make_object<tonlib_api::testGiver_accountState>(raw_state.balance, static_cast<td::uint32>(seqno),
to_transaction_id(raw_state.info));
}
td::Result<tonlib_api::object_ptr<tonlib_api::generic_AccountState>> to_generic_accountState(
RawAccountState&& raw_state) {
if (raw_state.code.is_null()) {
return tonlib_api::make_object<tonlib_api::generic_accountStateUninited>(
tonlib_api::make_object<tonlib_api::uninited_accountState>(raw_state.balance));
}
auto code_hash = raw_state.code->prefetch_ref()->get_hash();
if (code_hash == TestWallet::get_init_code_hash()) {
TRY_RESULT(test_wallet, to_testWallet_accountState(std::move(raw_state)));
return tonlib_api::make_object<tonlib_api::generic_accountStateTestWallet>(std::move(test_wallet));
}
if (code_hash == TestGiver::get_init_code_hash()) {
TRY_RESULT(test_wallet, to_testGiver_accountState(std::move(raw_state)));
return tonlib_api::make_object<tonlib_api::generic_accountStateTestGiver>(std::move(test_wallet));
}
TRY_RESULT(raw, to_raw_accountState(std::move(raw_state)));
return tonlib_api::make_object<tonlib_api::generic_accountStateRaw>(std::move(raw));
}
// Raw
td::Status TonlibClient::do_request(const tonlib_api::raw_getAccountAddress& request,
td::Promise<object_ptr<tonlib_api::accountAddress>>&& promise) {
TRY_RESULT(account_address, get_account_address(*request.initital_account_state_));
promise.set_value(tonlib_api::make_object<tonlib_api::accountAddress>(account_address.rserialize()));
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::raw_sendMessage& request,
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
td::Ref<vm::Cell> init_state;
if (!request.initial_account_state_.empty()) {
TRY_RESULT(new_init_state, vm::std_boc_deserialize(request.initial_account_state_));
init_state = std::move(new_init_state);
}
TRY_RESULT(data, vm::std_boc_deserialize(request.data_));
TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_));
auto message = GenericAccount::create_ext_message(account_address, std::move(init_state), std::move(data));
client_.send_query(ton::lite_api::liteServer_sendMessage(vm::std_boc_serialize(message).move_as_ok()),
[promise = std::move(promise)](auto r_info) mutable {
if (r_info.is_error()) {
promise.set_error(r_info.move_as_error());
} else {
auto info = r_info.move_as_ok();
LOG(ERROR) << "info: " << to_string(info);
promise.set_value(tonlib_api::make_object<tonlib_api::ok>());
}
});
return td::Status::OK();
}
td::Status TonlibClient::do_request(tonlib_api::raw_getAccountState& request,
td::Promise<object_ptr<tonlib_api::raw_accountState>>&& promise) {
if (!request.account_address_) {
return td::Status::Error(400, "Field account_address must not be empty");
}
TRY_RESULT(account_address, block::StdAddress::parse(request.account_address_->account_address_));
td::actor::create_actor<GetRawAccountState>(
"GetAccountState", client_.get_client(), std::move(account_address),
[promise = std::move(promise)](td::Result<RawAccountState> r_state) mutable {
if (r_state.is_error()) {
return promise.set_error(r_state.move_as_error());
}
promise.set_result(to_raw_accountState(r_state.move_as_ok()));
})
.release();
return td::Status::OK();
}
td::Status TonlibClient::do_request(tonlib_api::raw_getTransactions& request,
td::Promise<object_ptr<tonlib_api::raw_transactions>>&& promise) {
if (!request.account_address_) {
return td::Status::Error(400, "Field account_address must not be empty");
}
if (!request.from_transaction_id_) {
return td::Status::Error(400, "Field from_transaction_id must not be empty");
}
TRY_RESULT(account_address, block::StdAddress::parse(request.account_address_->account_address_));
auto lt = request.from_transaction_id_->lt_;
auto hash_str = request.from_transaction_id_->hash_;
if (hash_str.size() != 32) {
return td::Status::Error(400, "Invalid transaction id hash size");
}
td::Bits256 hash;
hash.as_slice().copy_from(hash_str);
td::actor::create_actor<GetTransactionHistory>(
"GetTransactionHistory", client_.get_client(), account_address, lt, hash,
[promise = std::move(promise)](td::Result<block::TransactionList::Info> r_info) mutable {
if (r_info.is_error()) {
return promise.set_error(r_info.move_as_error());
}
promise.set_result(to_raw_transactions(r_info.move_as_ok()));
})
.release();
return td::Status::OK();
}
td::Status TonlibClient::do_request(tonlib_api::testGiver_getAccountAddress& request,
td::Promise<object_ptr<tonlib_api::accountAddress>>&& promise) {
promise.set_value(tonlib_api::make_object<tonlib_api::accountAddress>(TestGiver::address().rserialize()));
return td::Status::OK();
}
td::Result<KeyStorage::InputKey> from_tonlib(tonlib_api::inputKey& input_key) {
if (!input_key.key_) {
return td::Status::Error(400, "Field key must not be empty");
}
return KeyStorage::InputKey{{td::SecureString(input_key.key_->public_key_), std::move(input_key.key_->secret_)},
std::move(input_key.local_password_)};
}
// TestWallet
td::Status TonlibClient::do_request(const tonlib_api::testWallet_init& request,
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
if (!request.private_key_) {
return td::Status::Error(400, "Field private_key must not be empty");
}
TRY_RESULT(input_key, from_tonlib(*request.private_key_));
auto init_state = TestWallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy()));
auto address = GenericAccount::get_address(0 /*zerochain*/, init_state);
TRY_RESULT(private_key, key_storage_.load_private_key(std::move(input_key)));
auto init_message = TestWallet::get_init_message(td::Ed25519::PrivateKey(std::move(private_key.private_key)));
return do_request(
tonlib_api::raw_sendMessage(tonlib_api::make_object<tonlib_api::accountAddress>(address.rserialize()),
vm::std_boc_serialize(init_state).move_as_ok().as_slice().str(),
vm::std_boc_serialize(init_message).move_as_ok().as_slice().str()),
std::move(promise));
}
td::Status TonlibClient::do_request(const tonlib_api::testWallet_getAccountAddress& request,
td::Promise<object_ptr<tonlib_api::accountAddress>>&& promise) {
TRY_RESULT(account_address, get_account_address(*request.initital_account_state_));
promise.set_value(tonlib_api::make_object<tonlib_api::accountAddress>(account_address.rserialize()));
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::testWallet_sendGrams& request,
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
if (!request.destination_) {
return td::Status::Error(400, "Field destination must not be empty");
}
if (!request.private_key_) {
return td::Status::Error(400, "Field private_key must not be empty");
}
TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_));
account_address.bounceable = false;
TRY_RESULT(input_key, from_tonlib(*request.private_key_));
auto address = GenericAccount::get_address(
0 /*zerochain*/, TestWallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy())));
TRY_RESULT(private_key, key_storage_.load_private_key(std::move(input_key)));
return do_request(
tonlib_api::raw_sendMessage(tonlib_api::make_object<tonlib_api::accountAddress>(address.rserialize()), "",
vm::std_boc_serialize(TestWallet::make_a_gift_message(
td::Ed25519::PrivateKey(std::move(private_key.private_key)),
request.seqno_, request.amount_, account_address))
.move_as_ok()
.as_slice()
.str()),
std::move(promise));
}
td::Status TonlibClient::do_request(tonlib_api::testWallet_getAccountState& request,
td::Promise<object_ptr<tonlib_api::testWallet_accountState>>&& promise) {
if (!request.account_address_) {
return td::Status::Error(400, "Field account_address must not be empty");
}
TRY_RESULT(account_address, block::StdAddress::parse(request.account_address_->account_address_));
td::actor::create_actor<GetRawAccountState>(
"GetAccountState", client_.get_client(), std::move(account_address),
[promise = std::move(promise)](td::Result<RawAccountState> r_state) mutable {
if (r_state.is_error()) {
return promise.set_error(r_state.move_as_error());
}
promise.set_result(to_testWallet_accountState(r_state.move_as_ok()));
})
.release();
return td::Status::OK();
}
// TestGiver
td::Status TonlibClient::do_request(const tonlib_api::testGiver_sendGrams& request,
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
if (!request.destination_) {
return td::Status::Error(400, "Field destination must not be empty");
}
TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_));
account_address.bounceable = false;
return do_request(
tonlib_api::raw_sendMessage(
tonlib_api::make_object<tonlib_api::accountAddress>(TestGiver::address().rserialize()), "",
vm::std_boc_serialize(TestGiver::make_a_gift_message(request.seqno_, request.amount_, account_address))
.move_as_ok()
.as_slice()
.str()),
std::move(promise));
}
td::Status TonlibClient::do_request(const tonlib_api::testGiver_getAccountState& request,
td::Promise<object_ptr<tonlib_api::testGiver_accountState>>&& promise) {
td::actor::create_actor<GetRawAccountState>(
"GetAccountState", client_.get_client(), TestGiver::address(),
[promise = std::move(promise)](td::Result<RawAccountState> r_state) mutable {
if (r_state.is_error()) {
return promise.set_error(r_state.move_as_error());
}
promise.set_result(to_testGiver_accountState(r_state.move_as_ok()));
})
.release();
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::generic_getAccountState& request,
td::Promise<object_ptr<tonlib_api::generic_AccountState>>&& promise) {
if (!request.account_address_) {
return td::Status::Error(400, "Field account_address must not be empty");
}
TRY_RESULT(account_address, block::StdAddress::parse(request.account_address_->account_address_));
td::actor::create_actor<GetRawAccountState>(
"GetAccountState", client_.get_client(), std::move(account_address),
[promise = std::move(promise)](td::Result<RawAccountState> r_state) mutable {
if (r_state.is_error()) {
return promise.set_error(r_state.move_as_error());
}
promise.set_result(to_generic_accountState(r_state.move_as_ok()));
})
.release();
return td::Status::OK();
}
td::Status TonlibClient::do_request(tonlib_api::generic_sendGrams& request,
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
TRY_RESULT(account_address, block::StdAddress::parse(request.source_->account_address_));
td::actor::create_actor<GetRawAccountState>(
"GetAccountState", client_.get_client(), std::move(account_address),
[promise = std::move(promise), self = this, actor_id = td::actor::actor_id(),
private_key = std::move(request.private_key_), destination = std::move(request.destination_),
amount = request.amount_](td::Result<RawAccountState> r_state) mutable {
if (r_state.is_error()) {
return promise.set_error(r_state.move_as_error());
}
auto rr_state = to_generic_accountState(r_state.move_as_ok());
if (rr_state.is_error()) {
return promise.set_error(rr_state.move_as_error());
}
auto state = rr_state.move_as_ok();
downcast_call(*state,
td::overloaded(
[&](tonlib_api::generic_accountStateTestGiver& test_giver_state) {
send_lambda(actor_id, [promise = std::move(promise), self,
query = tonlib_api::testGiver_sendGrams(
std::move(destination), test_giver_state.account_state_->seqno_,
amount)]() mutable {
auto status = self->do_request(query, std::move(promise));
if (status.is_error()) {
CHECK(promise);
promise.set_error(std::move(status));
}
});
return;
},
[&](tonlib_api::generic_accountStateTestWallet& test_wallet_state) {
send_lambda(actor_id, [promise = std::move(promise), self,
query = tonlib_api::testWallet_sendGrams(
std::move(private_key), std::move(destination),
test_wallet_state.account_state_->seqno_, amount)]() mutable {
auto status = self->do_request(query, std::move(promise));
if (status.is_error()) {
CHECK(promise);
promise.set_error(std::move(status));
}
});
},
[&](tonlib_api::generic_accountStateUninited&) {
promise.set_error(td::Status::Error(400, "Account is not inited"));
},
[&](tonlib_api::generic_accountStateRaw&) {
promise.set_error(td::Status::Error(400, "Unknown account type"));
}));
})
.release();
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::createNewKey& request,
td::Promise<object_ptr<tonlib_api::key>>&& promise) {
TRY_RESULT(key,
key_storage_.create_new_key(std::move(request.local_password_), std::move(request.mnemonic_password_)));
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key.public_key.as_slice().str(), std::move(key.secret)));
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::exportKey& request,
td::Promise<object_ptr<tonlib_api::exportedKey>>&& promise) {
if (!request.input_key_) {
return td::Status::Error(400, "Field input_key must not be empty");
}
TRY_RESULT(input_key, from_tonlib(*request.input_key_));
TRY_RESULT(exported_key, key_storage_.export_key(std::move(input_key)));
promise.set_value(tonlib_api::make_object<tonlib_api::exportedKey>(std::move(exported_key.mnemonic_words)));
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::deleteKey& request,
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
TRY_STATUS(key_storage_.delete_key(request.public_key_));
promise.set_value(tonlib_api::make_object<tonlib_api::ok>());
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::importKey& request,
td::Promise<object_ptr<tonlib_api::key>>&& promise) {
if (!request.exported_key_) {
return td::Status::Error(400, "Field exported_key must not be empty");
}
TRY_RESULT(key, key_storage_.import_key(std::move(request.local_password_), std::move(request.mnemonic_password_),
KeyStorage::ExportedKey{std::move(request.exported_key_->word_list_)}));
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key.public_key.as_slice().str(), std::move(key.secret)));
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::exportPemKey& request,
td::Promise<object_ptr<tonlib_api::exportedPemKey>>&& promise) {
if (!request.input_key_) {
return td::Status::Error(400, "Field input_key must not be empty");
}
if (!request.input_key_->key_) {
return td::Status::Error(400, "Field key must not be empty");
}
KeyStorage::InputKey input_key{
{td::SecureString(request.input_key_->key_->public_key_), std::move(request.input_key_->key_->secret_)},
std::move(request.input_key_->local_password_)};
TRY_RESULT(exported_pem_key, key_storage_.export_pem_key(std::move(input_key), std::move(request.key_password_)));
promise.set_value(tonlib_api::make_object<tonlib_api::exportedPemKey>(std::move(exported_pem_key.pem)));
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::importPemKey& request,
td::Promise<object_ptr<tonlib_api::key>>&& promise) {
if (!request.exported_key_) {
return td::Status::Error(400, "Field exported_key must not be empty");
}
TRY_RESULT(key, key_storage_.import_pem_key(std::move(request.local_password_), std::move(request.key_password_),
KeyStorage::ExportedPemKey{std::move(request.exported_key_->pem_)}));
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key.public_key.as_slice().str(), std::move(key.secret)));
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::exportEncryptedKey& request,
td::Promise<object_ptr<tonlib_api::exportedEncryptedKey>>&& promise) {
if (!request.input_key_) {
return td::Status::Error(400, "Field input_key must not be empty");
}
TRY_RESULT(input_key, from_tonlib(*request.input_key_));
TRY_RESULT(exported_key, key_storage_.export_encrypted_key(std::move(input_key), request.key_password_));
promise.set_value(tonlib_api::make_object<tonlib_api::exportedEncryptedKey>(std::move(exported_key.data)));
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::importEncryptedKey& request,
td::Promise<object_ptr<tonlib_api::key>>&& promise) {
if (!request.exported_encrypted_key_) {
return td::Status::Error(400, "Field exported_encrypted_key must not be empty");
}
TRY_RESULT(key, key_storage_.import_encrypted_key(
std::move(request.local_password_), std::move(request.key_password_),
KeyStorage::ExportedEncryptedKey{std::move(request.exported_encrypted_key_->data_)}));
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key.public_key.as_slice().str(), std::move(key.secret)));
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::changeLocalPassword& request,
td::Promise<object_ptr<tonlib_api::key>>&& promise) {
if (!request.input_key_) {
return td::Status::Error(400, "Field input_key must not be empty");
}
if (!request.input_key_->key_) {
return td::Status::Error(400, "Field key must not be empty");
}
KeyStorage::InputKey input_key{
{td::SecureString(request.input_key_->key_->public_key_), std::move(request.input_key_->key_->secret_)},
std::move(request.input_key_->local_password_)};
TRY_RESULT(key, key_storage_.change_local_password(std::move(input_key), std::move(request.new_local_password_)));
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key.public_key.as_slice().str(), std::move(key.secret)));
return td::Status::OK();
}
} // namespace tonlib

View file

@ -0,0 +1,136 @@
/*
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 "TonlibCallback.h"
#include "tonlib/Config.h"
#include "tonlib/ExtClient.h"
#include "tonlib/KeyStorage.h"
#include "td/actor/actor.h"
namespace tonlib {
class TonlibClient : public td::actor::Actor {
public:
template <class T>
using object_ptr = tonlib_api::object_ptr<T>;
explicit TonlibClient(td::unique_ptr<TonlibCallback> callback);
void request(td::uint64 id, object_ptr<tonlib_api::Function> function);
void close();
static object_ptr<tonlib_api::Object> static_request(object_ptr<tonlib_api::Function> function);
~TonlibClient();
private:
enum class State { Uninited, Running, Closed } state_ = State::Uninited;
td::unique_ptr<TonlibCallback> callback_;
Config config_;
// KeyStorage
KeyStorage key_storage_;
// network
td::actor::ActorOwn<ton::adnl::AdnlExtClient> raw_client_;
td::actor::ActorOwn<LastBlock> raw_last_block_;
ExtClient client_;
ExtClientRef get_client_ref();
void init_ext_client();
bool is_closing_{false};
td::uint32 ref_cnt_{1};
void hangup_shared() override {
ref_cnt_--;
try_stop();
}
void hangup() override;
void try_stop() {
if (is_closing_ && ref_cnt_ == 0) {
stop();
}
}
void on_result(td::uint64 id, object_ptr<tonlib_api::Object> response);
static bool is_static_request(td::int32 id);
static bool is_uninited_request(td::int32 id);
template <class T>
static object_ptr<tonlib_api::Object> do_static_request(const T& request) {
return tonlib_api::make_object<tonlib_api::error>(400, "Function can't be executed synchronously");
}
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::runTests& request);
template <class T, class P>
td::Status do_request(const T& request, P&& promise) {
return td::Status::Error(400, "Function is unsupported");
}
td::Status set_config(std::string config);
td::Status do_request(const tonlib_api::init& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
td::Status do_request(const tonlib_api::close& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
td::Status do_request(const tonlib_api::options_setConfig& request,
td::Promise<object_ptr<tonlib_api::ok>>&& promise);
td::Status do_request(const tonlib_api::raw_getAccountAddress& request,
td::Promise<object_ptr<tonlib_api::accountAddress>>&& promise);
td::Status do_request(const tonlib_api::raw_sendMessage& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
td::Status do_request(tonlib_api::raw_getAccountState& request,
td::Promise<object_ptr<tonlib_api::raw_accountState>>&& promise);
td::Status do_request(tonlib_api::raw_getTransactions& request,
td::Promise<object_ptr<tonlib_api::raw_transactions>>&& promise);
td::Status do_request(const tonlib_api::testWallet_init& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
td::Status do_request(const tonlib_api::testWallet_getAccountAddress& request,
td::Promise<object_ptr<tonlib_api::accountAddress>>&& promise);
td::Status do_request(const tonlib_api::testWallet_sendGrams& request,
td::Promise<object_ptr<tonlib_api::ok>>&& promise);
td::Status do_request(tonlib_api::testWallet_getAccountState& request,
td::Promise<object_ptr<tonlib_api::testWallet_accountState>>&& promise);
td::Status do_request(const tonlib_api::testGiver_getAccountState& request,
td::Promise<object_ptr<tonlib_api::testGiver_accountState>>&& promise);
td::Status do_request(tonlib_api::testGiver_getAccountAddress& request,
td::Promise<object_ptr<tonlib_api::accountAddress>>&& promise);
td::Status do_request(const tonlib_api::testGiver_sendGrams& request,
td::Promise<object_ptr<tonlib_api::ok>>&& promise);
td::Status do_request(const tonlib_api::generic_getAccountState& request,
td::Promise<object_ptr<tonlib_api::generic_AccountState>>&& promise);
td::Status do_request(tonlib_api::generic_sendGrams& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
td::Status do_request(const tonlib_api::createNewKey& request, td::Promise<object_ptr<tonlib_api::key>>&& promise);
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::importKey& request, td::Promise<object_ptr<tonlib_api::key>>&& promise);
td::Status do_request(const tonlib_api::exportPemKey& request,
td::Promise<object_ptr<tonlib_api::exportedPemKey>>&& promise);
td::Status do_request(const tonlib_api::importPemKey& request, td::Promise<object_ptr<tonlib_api::key>>&& promise);
td::Status do_request(const tonlib_api::exportEncryptedKey& request,
td::Promise<object_ptr<tonlib_api::exportedEncryptedKey>>&& promise);
td::Status do_request(const tonlib_api::importEncryptedKey& request,
td::Promise<object_ptr<tonlib_api::key>>&& promise);
td::Status do_request(const tonlib_api::changeLocalPassword& request,
td::Promise<object_ptr<tonlib_api::key>>&& promise);
};
} // namespace tonlib

View file

@ -0,0 +1,81 @@
/*
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
*/
#include "DecryptedKey.h"
#include "tonlib/keys/EncryptedKey.h"
#include "tonlib/keys/SimpleEncryption.h"
#include "td/utils/Random.h"
#include "td/utils/crypto.h"
namespace tonlib {
DecryptedKey::DecryptedKey(const Mnemonic &mnemonic)
: mnemonic_words(mnemonic.get_words()), private_key(mnemonic.to_private_key()) {
}
DecryptedKey::DecryptedKey(std::vector<td::SecureString> mnemonic_words, td::Ed25519::PrivateKey key)
: mnemonic_words(std::move(mnemonic_words)), private_key(std::move(key)) {
}
DecryptedKey::DecryptedKey(RawDecryptedKey key)
: DecryptedKey(std::move(key.mnemonic_words), td::Ed25519::PrivateKey(key.private_key.copy())) {
}
td::SecureString DecryptedKey::change_local_password(td::Slice secret_str, td::Slice old_local_password,
td::Slice new_local_password) {
CHECK(secret_str.size() == 32);
td::SecureString old_local_password_hash(32);
sha256(old_local_password, old_local_password_hash.as_mutable_slice());
td::SecureString new_local_password_hash(32);
sha256(new_local_password, new_local_password_hash.as_mutable_slice());
td::SecureString new_secret(32);
for (size_t i = 0; i < new_secret.size(); i++) {
new_secret.as_mutable_slice()[i] =
secret_str[i] ^ old_local_password_hash.as_slice()[i] ^ new_local_password_hash.as_slice()[i];
}
return new_secret;
}
EncryptedKey DecryptedKey::encrypt(td::Slice local_password, td::Slice old_secret) const {
LOG(ERROR) << "encrypt";
td::SecureString secret(32);
if (old_secret.size() == td::as_slice(secret).size()) {
secret.as_mutable_slice().copy_from(old_secret);
} else {
td::Random::secure_bytes(secret.as_mutable_slice());
}
td::SecureString local_password_hash(32);
sha256(local_password, local_password_hash.as_mutable_slice());
td::SecureString decrypted_secret(32);
for (size_t i = 0; i < decrypted_secret.size(); i++) {
decrypted_secret.as_mutable_slice()[i] = secret.as_slice()[i] ^ local_password_hash.as_slice()[i];
}
td::SecureString encryption_secret(64);
pbkdf2_sha512(as_slice(decrypted_secret), "TON local key", PBKDF_ITERATIONS, encryption_secret.as_mutable_slice());
std::vector<td::SecureString> mnemonic_words_copy;
for (auto &w : mnemonic_words) {
mnemonic_words_copy.push_back(w.copy());
}
auto data = td::serialize_secure(RawDecryptedKey{std::move(mnemonic_words_copy), private_key.as_octet_string()});
auto encrypted_data = SimpleEncryption::encrypt_data(data, as_slice(encryption_secret));
return EncryptedKey{std::move(encrypted_data), private_key.get_public_key().move_as_ok(), std::move(secret)};
}
} // namespace tonlib

View file

@ -0,0 +1,64 @@
/*
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 <vector>
#include <string>
#include "td/utils/tl_helpers.h"
#include "td/utils/SharedSlice.h"
#include "tonlib/keys/Mnemonic.h"
namespace tonlib {
struct RawDecryptedKey {
std::vector<td::SecureString> mnemonic_words;
td::SecureString private_key;
template <class StorerT>
void store(StorerT &storer) const {
using td::store;
store(mnemonic_words, storer);
store(private_key, storer);
}
template <class ParserT>
void parse(ParserT &parser) {
using td::parse;
parse(mnemonic_words, parser);
parse(private_key, parser);
}
};
struct EncryptedKey;
struct DecryptedKey {
DecryptedKey() = default;
explicit DecryptedKey(const Mnemonic &mnemonic);
DecryptedKey(std::vector<td::SecureString> mnemonic_words, td::Ed25519::PrivateKey key);
DecryptedKey(RawDecryptedKey key);
std::vector<td::SecureString> mnemonic_words;
td::Ed25519::PrivateKey private_key;
static td::SecureString change_local_password(td::Slice secret, td::Slice old_local_password,
td::Slice new_local_password);
EncryptedKey encrypt(td::Slice local_password, td::Slice secret = {}) const;
};
} // namespace tonlib

View file

@ -0,0 +1,53 @@
/*
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
*/
#include "EncryptedKey.h"
#include "tonlib/keys/DecryptedKey.h"
#include "tonlib/keys/SimpleEncryption.h"
#include "td/utils/crypto.h"
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");
}
td::SecureString local_password_hash(32);
sha256(local_password, local_password_hash.as_mutable_slice());
td::SecureString decrypted_secret(32);
for (size_t i = 0; i < 32; i++) {
decrypted_secret.as_mutable_slice()[i] = secret.as_slice()[i] ^ local_password_hash.as_slice()[i];
}
td::SecureString encryption_secret(64);
pbkdf2_sha512(as_slice(decrypted_secret), "TON local key", PBKDF_ITERATIONS, encryption_secret.as_mutable_slice());
TRY_RESULT(decrypted_data, SimpleEncryption::decrypt_data(as_slice(encrypted_data), as_slice(encryption_secret)));
RawDecryptedKey raw_decrypted_key;
TRY_STATUS(td::unserialize(raw_decrypted_key, decrypted_data));
DecryptedKey res(std::move(raw_decrypted_key));
TRY_RESULT(got_public_key, res.private_key.get_public_key());
if (check_public_key &&
got_public_key.as_octet_string().as_slice() != this->public_key.as_octet_string().as_slice()) {
return td::Status::Error("Something wrong: public key of decrypted private key differs from requested public key");
}
return std::move(res);
}
} // namespace tonlib

View file

@ -0,0 +1,38 @@
/*
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 "crypto/Ed25519.h"
#include <string>
namespace tonlib {
constexpr int PBKDF_ITERATIONS = 100000;
struct DecryptedKey;
struct EncryptedKey {
td::SecureString encrypted_data;
td::Ed25519::PublicKey public_key;
td::SecureString secret;
td::Result<DecryptedKey> decrypt(td::Slice local_password, bool check_public_key = true);
};
} // namespace tonlib

View file

@ -0,0 +1,188 @@
/*
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
*/
#include "Mnemonic.h"
#include "tonlib/keys/bip39.h"
#include <vector>
#include "td/utils/crypto.h"
#include "td/utils/format.h"
#include "td/utils/Random.h"
#include "td/utils/Span.h"
#include "td/utils/misc.h"
#include "td/utils/optional.h"
#include "crypto/Ed25519.h"
namespace tonlib {
td::Result<Mnemonic> Mnemonic::create(td::SecureString words, td::SecureString password) {
return create_from_normalized(normalize_and_split(std::move(words)), std::move(password));
}
td::Result<Mnemonic> Mnemonic::create(std::vector<td::SecureString> words, td::SecureString password) {
return create(join(words), std::move(password));
}
td::Result<Mnemonic> Mnemonic::create_from_normalized(std::vector<td::SecureString> words, td::SecureString password) {
auto new_words = normalize_and_split(join(words));
if (new_words != words) {
return td::Status::Error("Mnemonic string is not normalized");
}
return Mnemonic(std::move(words), std::move(password));
}
td::SecureString Mnemonic::to_entropy() const {
td::SecureString res(64);
td::hmac_sha512(join(words_), password_, res.as_mutable_slice());
return res;
}
td::SecureString Mnemonic::to_seed() const {
td::SecureString hash(64);
td::pbkdf2_sha512(as_slice(to_entropy()), "TON default seed", PBKDF_ITERATIONS, hash.as_mutable_slice());
return hash;
}
td::Ed25519::PrivateKey Mnemonic::to_private_key() const {
return td::Ed25519::PrivateKey(td::SecureString(as_slice(to_seed()).substr(0, td::Ed25519::PrivateKey::LENGTH)));
}
bool Mnemonic::is_basic_seed() {
td::SecureString hash(64);
td::pbkdf2_sha512(as_slice(to_entropy()), "TON seed version", td::max(1, PBKDF_ITERATIONS / 256),
hash.as_mutable_slice());
return hash.as_slice()[0] == 0;
}
bool Mnemonic::is_password_seed() {
td::SecureString hash(64);
td::pbkdf2_sha512(as_slice(to_entropy()), "TON fast seed version", 1, hash.as_mutable_slice());
return hash.as_slice()[0] == 1;
}
std::vector<td::SecureString> Mnemonic::get_words() const {
std::vector<td::SecureString> res;
for (auto &word : words_) {
res.push_back(word.copy());
}
return res;
}
std::vector<td::SecureString> Mnemonic::normalize_and_split(td::SecureString words) {
for (auto &c : words.as_mutable_slice()) {
if (td::is_alpha(c)) {
c = td::to_lower(c);
} else {
c = ' ';
}
}
auto vec = td::full_split(words.as_slice(), ' ');
std::vector<td::SecureString> res;
for (auto &s : vec) {
if (!s.empty()) {
res.push_back(td::SecureString(s));
}
}
return res;
}
td::StringBuilder &operator<<(td::StringBuilder &sb, const Mnemonic &mnemonic) {
sb << "Mnemonic" << td::format::as_array(mnemonic.words_);
if (!mnemonic.password_.empty()) {
sb << " with password[" << mnemonic.password_ << "]";
}
return sb;
}
Mnemonic::Mnemonic(std::vector<td::SecureString> words, td::SecureString password)
: words_(std::move(words)), password_(std::move(password)) {
}
td::SecureString Mnemonic::join(td::Span<td::SecureString> words) {
size_t res_size = 0;
for (size_t i = 0; i < words.size(); i++) {
if (i != 0) {
res_size++;
}
res_size += words[i].size();
}
td::SecureString res(res_size);
auto dst = res.as_mutable_slice();
for (size_t i = 0; i < words.size(); i++) {
if (i != 0) {
dst[0] = ' ';
dst.remove_prefix(1);
}
dst.copy_from(words[i].as_slice());
dst.remove_prefix(words[i].size());
}
return res;
}
td::Result<Mnemonic> Mnemonic::create_new(Options options) {
if (options.words_count == 0) {
options.words_count = 24;
}
if (options.words_count < 8 || options.words_count > 48) {
return td::Status::Error(PSLICE() << "Invalid words count(" << options.words_count
<< ") requested for mnemonic creation");
}
td::int32 max_iterations = 256 * 20;
if (!options.password.empty()) {
max_iterations *= 256;
}
auto bip_words = Mnemonic::normalize_and_split(td::SecureString(bip39_english()));
CHECK(bip_words.size() == 2048);
int A = 0, B = 0, C = 0;
for (int iteration = 0; iteration < max_iterations; iteration++) {
std::vector<td::SecureString> words;
for (int i = 0; i < options.words_count; i++) {
words.push_back(bip_words[td::Random::secure_int32() & 2047].copy());
}
bool has_password = !options.password.empty();
td::optional<Mnemonic> mnemonic_without_password;
if (has_password) {
auto copy_words = td::transform(words, [](auto &w) { return w.copy(); });
mnemonic_without_password = Mnemonic::create(std::move(copy_words), {}).move_as_ok();
if (!mnemonic_without_password.value().is_password_seed()) {
A++;
continue;
}
}
auto mnemonic = Mnemonic::create(std::move(words), options.password.copy()).move_as_ok();
if (!mnemonic.is_basic_seed()) {
B++;
continue;
}
if (has_password && mnemonic_without_password.value().is_basic_seed()) {
C++;
continue;
}
LOG(INFO) << "Mnemonic generation debug stats: " << A << " " << B << " " << C;
return std::move(mnemonic);
}
return td::Status::Error("Failed to create a mnemonic (should not happen)");
}
} // namespace tonlib

View file

@ -0,0 +1,65 @@
/*
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 "crypto/Ed25519.h"
#include "td/utils/int_types.h"
#include "td/utils/Span.h"
#include "td/utils/Status.h"
#include "td/utils/UInt.h"
namespace tonlib {
class Mnemonic {
public:
static constexpr int PBKDF_ITERATIONS = 100000;
static td::Result<Mnemonic> create(td::SecureString words, td::SecureString password);
static td::Result<Mnemonic> create(std::vector<td::SecureString> words, td::SecureString password);
struct Options {
Options() {
}
int words_count = 24;
td::SecureString password;
};
static td::Result<Mnemonic> create_new(Options options = {});
td::SecureString to_entropy() const;
td::SecureString to_seed() const;
td::Ed25519::PrivateKey to_private_key() const;
bool is_basic_seed();
bool is_password_seed();
std::vector<td::SecureString> get_words() const;
static std::vector<td::SecureString> normalize_and_split(td::SecureString words);
private:
std::vector<td::SecureString> words_;
td::SecureString password_;
Mnemonic(std::vector<td::SecureString> words, td::SecureString password);
static td::SecureString join(td::Span<td::SecureString> words);
static td::Result<Mnemonic> create_from_normalized(std::vector<td::SecureString> words, td::SecureString password);
friend td::StringBuilder &operator<<(td::StringBuilder &sb, const Mnemonic &mnemonic);
};
} // namespace tonlib

View file

@ -0,0 +1,101 @@
/*
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
*/
#include "SimpleEncryption.h"
#include "td/utils/Random.h"
#include "td/utils/SharedSlice.h"
namespace tonlib {
td::AesCbcState SimpleEncryption::calc_aes_cbc_state_hash(td::Slice hash) {
CHECK(hash.size() == 64);
td::SecureString key(32);
key.as_mutable_slice().copy_from(hash.substr(0, 32));
td::SecureString iv(16);
iv.as_mutable_slice().copy_from(hash.substr(32, 16));
return td::AesCbcState{key, iv};
}
td::AesCbcState SimpleEncryption::calc_aes_cbc_state_sha512(td::Slice seed) {
td::SecureString hash(64);
sha512(seed, hash.as_mutable_slice());
return calc_aes_cbc_state_hash(hash.as_slice());
}
td::SecureString SimpleEncryption::gen_random_prefix(td::int64 data_size) {
td::SecureString buff(td::narrow_cast<size_t>(((32 + 15 + data_size) & -16) - data_size), 0);
td::Random::secure_bytes(buff.as_mutable_slice());
buff.as_mutable_slice()[0] = td::narrow_cast<td::uint8>(buff.size());
CHECK((buff.size() + data_size) % 16 == 0);
return buff;
}
td::SecureString SimpleEncryption::combine_secrets(td::Slice a, td::Slice b) {
td::SecureString res(64, 0);
hmac_sha512(a, b, res.as_mutable_slice());
return res;
}
td::SecureString SimpleEncryption::encrypt_data_with_prefix(td::Slice data, td::Slice secret) {
CHECK(data.size() % 16 == 0);
auto data_hash = sha256(data);
td::SecureString res_buf(data.size() + 32, 0);
auto res = res_buf.as_mutable_slice();
res.copy_from(data_hash);
auto cbc_state = calc_aes_cbc_state_hash(combine_secrets(data_hash, secret));
cbc_state.encrypt(data, res.substr(32));
return res_buf;
}
td::SecureString SimpleEncryption::encrypt_data(td::Slice data, td::Slice secret) {
auto prefix = gen_random_prefix(data.size());
td::SecureString combined(prefix.size() + data.size());
combined.as_mutable_slice().copy_from(prefix);
combined.as_mutable_slice().substr(prefix.size()).copy_from(data);
return encrypt_data_with_prefix(combined.as_slice(), secret);
}
td::Result<td::SecureString> SimpleEncryption::decrypt_data(td::Slice encrypted_data, td::Slice secret) {
if (encrypted_data.size() < 33) {
return td::Status::Error("Failed to decrypt: data is too small");
}
if (encrypted_data.size() % 16 != 0) {
return td::Status::Error("Failed to decrypt: data size is not divisible by 32");
}
auto data_hash = encrypted_data.substr(0, 32);
encrypted_data = encrypted_data.substr(32);
auto cbc_state = calc_aes_cbc_state_hash(combine_secrets(data_hash, secret));
td::SecureString decrypted_data(encrypted_data.size(), 0);
cbc_state.decrypt(encrypted_data, decrypted_data.as_mutable_slice());
// check hash
if (data_hash != td::sha256(decrypted_data)) {
return td::Status::Error("Failed to decrypt: hash mismatch");
}
td::uint8 prefix_size = static_cast<td::uint8>(decrypted_data[0]);
if (prefix_size > decrypted_data.size() || prefix_size < 32) {
return td::Status::Error("Failed to decrypt: invalid prefix size");
}
return td::SecureString(decrypted_data.as_slice().substr(prefix_size));
}
} // namespace tonlib

View file

@ -0,0 +1,39 @@
/*
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/crypto.h"
#include "td/utils/Slice.h"
#include "td/utils/SharedSlice.h"
namespace tonlib {
class SimpleEncryption {
public:
static td::SecureString encrypt_data(td::Slice data, td::Slice secret);
static td::Result<td::SecureString> decrypt_data(td::Slice encrypted_data, td::Slice secret);
private:
static td::AesCbcState calc_aes_cbc_state_hash(td::Slice hash);
static td::AesCbcState calc_aes_cbc_state_sha512(td::Slice seed);
static td::SecureString gen_random_prefix(td::int64 data_size);
static td::SecureString combine_secrets(td::Slice a, td::Slice b);
static td::SecureString encrypt_data_with_prefix(td::Slice data, td::Slice secret);
};
} // namespace tonlib

2072
tonlib/tonlib/keys/bip39.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,24 @@
/*
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/Slice.h"
namespace tonlib {
td::CSlice bip39_english();
}

View file

@ -0,0 +1,57 @@
/*
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
*/
#include "tonlib/tonlib_client_json.h"
#include "tonlib/ClientJson.h"
#include "td/utils/Slice.h"
extern "C" int tonlib_client_json_square(int x, const char *str) {
return x * x;
}
void *tonlib_client_json_create() {
return new tonlib::ClientJson();
}
void tonlib_client_json_destroy(void *client) {
delete static_cast<tonlib::ClientJson *>(client);
}
void tonlib_client_json_send(void *client, const char *request) {
static_cast<tonlib::ClientJson *>(client)->send(td::Slice(request == nullptr ? "" : request));
}
const char *tonlib_client_json_receive(void *client, double timeout) {
auto slice = static_cast<tonlib::ClientJson *>(client)->receive(timeout);
if (slice.empty()) {
return nullptr;
} else {
return slice.c_str();
}
}
const char *tonlib_client_json_execute(void *client, const char *request) {
auto slice = tonlib::ClientJson::execute(td::Slice(request == nullptr ? "" : request));
if (slice.empty()) {
return nullptr;
} else {
return slice.c_str();
}
}

View file

@ -0,0 +1,39 @@
/*
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 "tonlib/tonlibjson_export.h"
#ifdef __cplusplus
extern "C" {
#endif
TONLIBJSON_EXPORT void *tonlib_client_json_create();
TONLIBJSON_EXPORT void tonlib_client_json_send(void *client, const char *request);
TONLIBJSON_EXPORT const char *tonlib_client_json_receive(void *client, double timeout);
TONLIBJSON_EXPORT const char *tonlib_client_json_execute(void *client, const char *request);
TONLIBJSON_EXPORT void tonlib_client_json_destroy(void *client);
#ifdef __cplusplus
} // extern "C"
#endif

35
tonlib/tonlib/utils.cpp Normal file
View file

@ -0,0 +1,35 @@
/*
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
*/
#include "tonlib/utils.h"
#include "td/utils/misc.h"
#include "vm/cellslice.h"
namespace tonlib {
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, "}")) {
return td::Status::Error("Invalid binary bitstring constant");
}
int bits =
(int)td::bitstring::parse_bitstring_binary_literal(buff, sizeof(buff), literal.begin() + 2, literal.end() - 1);
if (bits < 0) {
return td::Status::Error("Invalid binary bitstring constant");
}
return td::Ref<vm::CellSlice>{true, vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()};
}
} // namespace tonlib

26
tonlib/tonlib/utils.h Normal file
View file

@ -0,0 +1,26 @@
/*
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 "vm/cells.h"
#include "ton/ton-types.h"
#include "block/block.h"
#include "block/block-parse.h"
namespace tonlib {
td::Result<td::Ref<vm::CellSlice>> binary_bitstring_to_cellslice(td::Slice literal);
} // namespace tonlib

View file

@ -0,0 +1,5 @@
_tonlib_client_json_create
_tonlib_client_json_destroy
_tonlib_client_json_send
_tonlib_client_json_receive
_tonlib_client_json_execute