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

tonlib: big update

This commit is contained in:
ton 2019-09-30 12:53:00 +04:00
parent fd7a8de970
commit ecb3e06a06
37 changed files with 581 additions and 90 deletions

View file

@ -5,6 +5,7 @@ if (NOT OPENSSL_FOUND)
endif()
set(TONLIB_SOURCE
tonlib/CellString.cpp
tonlib/Client.cpp
tonlib/Config.cpp
tonlib/ExtClient.cpp
@ -12,6 +13,7 @@ set(TONLIB_SOURCE
tonlib/ExtClientOutbound.cpp
tonlib/GenericAccount.cpp
tonlib/KeyStorage.cpp
tonlib/KeyValue.cpp
tonlib/LastBlock.cpp
tonlib/LastBlockStorage.cpp
tonlib/Logging.cpp
@ -21,6 +23,7 @@ set(TONLIB_SOURCE
tonlib/utils.cpp
tonlib/Wallet.cpp
tonlib/CellString.h
tonlib/Client.h
tonlib/Config.h
tonlib/ExtClient.h
@ -28,6 +31,7 @@ set(TONLIB_SOURCE
tonlib/ExtClientOutbound.h
tonlib/GenericAccount.h
tonlib/KeyStorage.h
tonlib/KeyValue.h
tonlib/LastBlock.h
tonlib/LastBlockStorage.h
tonlib/Logging.h

View file

@ -0,0 +1,3 @@
include(CMakeFindDependencyMacro)
#TODO: write all external dependencies
include("${CMAKE_CURRENT_LIST_DIR}/TonlibTargets.cmake")

View file

@ -28,6 +28,7 @@
#include "vm/boc.h"
#include "vm/cells/MerkleProof.h"
#include "tonlib/CellString.h"
#include "tonlib/utils.h"
#include "tonlib/TestGiver.h"
#include "tonlib/TestWallet.h"
@ -53,6 +54,20 @@
#include "tonlib/keys/Mnemonic.h"
#include "tonlib/keys/SimpleEncryption.h"
TEST(Tonlib, CellString) {
for (unsigned size :
{0, 1, 7, 8, 35, 127, 128, 255, 256, (int)vm::CellString::max_bytes - 1, (int)vm::CellString::max_bytes}) {
auto str = td::rand_string('a', 'z', size);
for (unsigned head : {0, 1, 7, 8, 127, 35 * 8, 127 * 8, 1023, 1024}) {
vm::CellBuilder cb;
vm::CellString::store(cb, str, head).ensure();
auto cs = vm::load_cell_slice(cb.finalize());
auto got_str = vm::CellString::load(cs, head).move_as_ok();
ASSERT_EQ(str, got_str);
}
}
};
using namespace tonlib;
std::string current_dir() {
@ -268,20 +283,23 @@ static auto sync_send = [](auto &client, auto query) {
TEST(Tonlib, InitClose) {
using tonlib_api::make_object;
auto cfg = [](auto str) { return make_object<tonlib_api::config>(str, "", false, false); };
auto dir = [](auto str) { return make_object<tonlib_api::keyStoreTypeDirectory>(str); };
{
Client client;
sync_send(client, make_object<tonlib_api::close>()).ensure();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure_error();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, dir("."))))
.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>(cfg("fdajkfldsjkafld"), ".")))
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(cfg("fdajkfldsjkafld"), dir("."))))
.ensure_error();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "fdhskfds")))
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, dir("fdhskfds"))))
.ensure_error();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, dir(".")))).ensure();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, dir("."))))
.ensure_error();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure_error();
td::Slice bad_config = R"abc(
{
@ -294,7 +312,8 @@ TEST(Tonlib, InitClose) {
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>(nullptr, "."))).ensure_error();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, dir("."))))
.ensure_error();
}
}
@ -389,7 +408,9 @@ TEST(Tonlib, ParseAddres) {
Client client;
// init
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure();
sync_send(client, make_object<tonlib_api::init>(
make_object<tonlib_api::options>(nullptr, make_object<tonlib_api::keyStoreTypeDirectory>("."))))
.ensure();
sync_send(client, make_object<tonlib_api::unpackAccountAddress>("hello")).ensure_error();
auto addr =
@ -409,7 +430,9 @@ TEST(Tonlib, KeysApi) {
Client client;
// init
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure();
sync_send(client, make_object<tonlib_api::init>(
make_object<tonlib_api::options>(nullptr, make_object<tonlib_api::keyStoreTypeDirectory>("."))))
.ensure();
auto local_password = td::SecureString("local password");
auto mnemonic_password = td::SecureString("mnemonic password");
{

View file

@ -197,7 +197,8 @@ int main(int argc, char* argv[]) {
Client client;
{
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(
make_object<tonlib_api::config>(global_config_str, "", false, false), ".")))
make_object<tonlib_api::config>(global_config_str, "", false, false),
make_object<tonlib_api::keyStoreTypeDirectory>("."))))
.ensure();
}
//dump_transaction_history(client, get_test_giver_address(client));
@ -211,7 +212,8 @@ int main(int argc, char* argv[]) {
{
// init
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(
make_object<tonlib_api::config>(global_config_str, "", false, false), ".")))
make_object<tonlib_api::config>(global_config_str, "", false, false),
make_object<tonlib_api::keyStoreTypeDirectory>("."))))
.ensure();
auto key = sync_send(client, make_object<tonlib_api::createNewKey>(

View file

@ -0,0 +1,64 @@
#include "CellString.h"
#include "td/utils/misc.h"
#include "vm/cells/CellSlice.h"
namespace vm {
td::Status CellString::store(CellBuilder &cb, td::Slice slice, unsigned int top_bits) {
td::uint32 size = td::narrow_cast<td::uint32>(slice.size() * 8);
return store(cb, td::BitSlice(slice.ubegin(), size), top_bits);
}
td::Status CellString::store(CellBuilder &cb, td::BitSlice slice, unsigned int top_bits) {
if (slice.size() > max_bytes * 8) {
return td::Status::Error("String is too long (1)");
}
unsigned int head = td::min(slice.size(), td::min(cb.remaining_bits(), top_bits)) / 8 * 8;
auto max_bits = vm::Cell::max_bits / 8 * 8;
auto depth = 1 + (slice.size() - head + max_bits - 1) / max_bits;
if (depth > max_chain_length) {
return td::Status::Error("String is too long (2)");
}
cb.append_bitslice(slice.subslice(0, head));
slice.advance(head);
if (slice.size() == 0) {
return td::Status::OK();
}
CellBuilder child_cb;
store(child_cb, std::move(slice));
cb.store_ref(child_cb.finalize());
return td::Status::OK();
}
template <class F>
void CellString::for_each(F &&f, CellSlice &cs, unsigned int top_bits) {
unsigned int head = td::min(cs.size(), top_bits);
f(cs.prefetch_bits(head));
if (!cs.have_refs()) {
return;
}
auto ref = cs.prefetch_ref();
while (true) {
auto cs = vm::load_cell_slice(ref);
f(cs.prefetch_bits(cs.size()));
if (!cs.have_refs()) {
return;
}
ref = cs.prefetch_ref();
}
}
td::Result<td::string> CellString::load(CellSlice &cs, unsigned int top_bits) {
unsigned int size = 0;
for_each([&](auto slice) { size += slice.size(); }, cs, top_bits);
if (size % 8 != 0) {
return td::Status::Error("Size is not divisible by 8");
}
std::string res(size / 8, 0);
td::BitPtr to(td::MutableSlice(res).ubegin());
for_each([&](auto slice) { to.concat(slice); }, cs, top_bits);
CHECK(to.offs == (int)size);
return res;
}
} // namespace vm

View file

@ -0,0 +1,22 @@
#pragma once
#include "td/utils/Status.h"
#include "vm/cells/CellBuilder.h"
namespace vm {
class CellString {
public:
static constexpr unsigned int max_bytes = 1024;
static constexpr unsigned int max_chain_length = 16;
static td::Status store(CellBuilder &cb, td::Slice slice, unsigned int top_bits = Cell::max_bits);
static td::Status store(CellBuilder &cb, td::BitSlice slice, unsigned int top_bits = Cell::max_bits);
static td::Result<td::string> load(CellSlice &cs, unsigned int top_bits = Cell::max_bits);
private:
template <class F>
static void for_each(F &&f, CellSlice &cs, unsigned int top_bits = Cell::max_bits);
};
} // namespace vm

View file

@ -27,29 +27,18 @@
#include "td/utils/crypto.h"
namespace tonlib {
namespace {
std::string to_file_name_old(const KeyStorage::Key &key) {
return td::buffer_to_hex(key.public_key);
}
std::string KeyStorage::to_file_path_old(const Key &key) {
return directory_ + TD_DIR_SLASH + to_file_name_old(key);
}
std::string to_file_name(const KeyStorage::Key &key) {
return td::buffer_to_hex(td::sha512(key.secret.as_slice()).substr(0, 32));
}
} // namespace
std::string KeyStorage::to_file_path(const Key &key) {
return directory_ + TD_DIR_SLASH + to_file_name(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();
void KeyStorage::set_key_value(std::shared_ptr<KeyValue> kv) {
kv_ = std::move(kv);
}
td::Result<KeyStorage::Key> KeyStorage::save_key(const DecryptedKey &decrypted_key, td::Slice local_password) {
@ -58,17 +47,7 @@ td::Result<KeyStorage::Key> KeyStorage::save_key(const DecryptedKey &decrypted_k
Key res;
res.public_key = encrypted_key.public_key.as_octet_string();
res.secret = std::move(encrypted_key.secret);
auto size = encrypted_key.encrypted_data.size();
LOG(ERROR) << "SAVE " << to_file_name(res);
TRY_RESULT(to_file, td::FileFd::open(to_file_path(res), 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();
TRY_STATUS(kv_->set(to_file_name(res), encrypted_key.encrypted_data));
return std::move(res);
}
@ -83,13 +62,14 @@ td::Result<KeyStorage::Key> KeyStorage::create_new_key(td::Slice local_password,
}
td::Result<DecryptedKey> KeyStorage::export_decrypted_key(InputKey input_key) {
auto r_encrypted_data = td::read_file_secure(to_file_path(input_key.key));
auto r_encrypted_data = kv_->get(to_file_name(input_key.key));
if (r_encrypted_data.is_error()) {
r_encrypted_data = td::read_file_secure(to_file_path_old(input_key.key));
r_encrypted_data = kv_->get(to_file_name_old(input_key.key));
if (r_encrypted_data.is_ok()) {
LOG(WARNING) << "Restore private from deprecated location " << to_file_path_old(input_key.key) << " --> "
<< to_file_path(input_key.key);
td::rename(to_file_path_old(input_key.key), to_file_path(input_key.key)).ignore();
LOG(WARNING) << "Restore private from deprecated location " << to_file_name_old(input_key.key) << " --> "
<< to_file_name(input_key.key);
TRY_STATUS(kv_->set(to_file_name(input_key.key), r_encrypted_data.ok()));
kv_->erase(to_file_name_old(input_key.key)).ignore();
}
}
TRY_RESULT(encrypted_data, std::move(r_encrypted_data));
@ -113,7 +93,7 @@ td::Result<KeyStorage::PrivateKey> KeyStorage::load_private_key(InputKey input_k
}
td::Status KeyStorage::delete_key(const Key &key) {
return td::unlink(to_file_path(key));
return kv_->erase(to_file_name(key));
}
td::Result<KeyStorage::Key> KeyStorage::import_key(td::Slice local_password, td::Slice mnemonic_password,
@ -140,7 +120,8 @@ td::Result<KeyStorage::Key> KeyStorage::change_local_password(InputKey input_key
Key res;
res.public_key = std::move(input_key.key.public_key);
res.secret = std::move(new_secret);
TRY_STATUS(td::copy_file(to_file_path(input_key.key), to_file_path(res)));
TRY_RESULT(value, kv_->get(to_file_name(input_key.key)));
TRY_STATUS(kv_->add(to_file_name(res), value));
return std::move(res);
}

View file

@ -21,6 +21,8 @@
#include "td/utils/Status.h"
#include "td/utils/SharedSlice.h"
#include "KeyValue.h"
#include <string>
namespace tonlib {
@ -48,7 +50,7 @@ class KeyStorage {
td::SecureString private_key;
};
td::Status set_directory(std::string directory);
void set_key_value(std::shared_ptr<KeyValue> kv);
td::Result<Key> create_new_key(td::Slice local_password, td::Slice key_password, td::Slice entropy);
@ -67,12 +69,9 @@ class KeyStorage {
td::Result<PrivateKey> load_private_key(InputKey input_key);
private:
std::string directory_;
std::shared_ptr<KeyValue> kv_;
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(const Key& key);
std::string to_file_path_old(const Key& key);
};
} // namespace tonlib

103
tonlib/tonlib/KeyValue.cpp Normal file
View file

@ -0,0 +1,103 @@
#include "KeyValue.h"
#include "td/utils/filesystem.h"
#include "td/utils/port/path.h"
#include <map>
#include <utility>
namespace tonlib {
namespace detail {
class KeyValueDir : public KeyValue {
public:
static td::Result<td::unique_ptr<KeyValueDir>> create(td::CSlice directory) {
TRY_RESULT(path, td::realpath(directory));
TRY_RESULT(stat, td::stat(path));
if (!stat.is_dir_) {
return td::Status::Error("not a directory");
}
return td::make_unique<KeyValueDir>(path);
}
KeyValueDir(std::string directory) : directory_(std::move(directory)) {
}
td::Status add(td::Slice key, td::Slice value) override {
auto path = to_file_path(key.str());
if (td::stat(path).is_ok()) {
return td::Status::Error(PSLICE() << "File " << path << "already exists");
}
return td::atomic_write_file(path, value);
}
td::Status set(td::Slice key, td::Slice value) override {
return td::atomic_write_file(to_file_path(key.str()), value);
}
td::Result<td::SecureString> get(td::Slice key) override {
return td::read_file_secure(to_file_path(key.str()));
}
td::Status erase(td::Slice key) override {
return td::unlink(key.str());
}
private:
std::string directory_;
std::string to_file_path(std::string key) {
return directory_ + TD_DIR_SLASH + key;
}
};
class KeyValueInmemory : public KeyValue {
public:
td::Status add(td::Slice key, td::Slice value) override {
auto res = map_.insert(std::make_pair(key.str(), td::SecureString(value)));
if (!res.second) {
return td::Status::Error(PSLICE() << "Add failed: value with key=`" << key << "` already exists");
}
return td::Status::OK();
}
td::Status set(td::Slice key, td::Slice value) override {
map_[key.str()] = td::SecureString(value);
return td::Status::OK();
}
td::Result<td::SecureString> get(td::Slice key) override {
auto it = map_.find(key);
if (it == map_.end()) {
return td::Status::Error("Unknown key");
}
return it->second.copy();
}
static td::Result<td::unique_ptr<KeyValueInmemory>> create() {
return td::make_unique<KeyValueInmemory>();
}
td::Status erase(td::Slice key) override {
auto it = map_.find(key);
if (it == map_.end()) {
return td::Status::Error("Unknown key");
}
map_.erase(it);
return td::Status::OK();
}
private:
class Cmp : public std::less<> {
public:
using is_transparent = void;
};
std::map<std::string, td::SecureString, Cmp> map_;
};
} // namespace detail
td::Result<td::unique_ptr<KeyValue>> KeyValue::create_dir(td::CSlice dir) {
TRY_RESULT(res, detail::KeyValueDir::create(dir.str()));
return std::move(res);
}
td::Result<td::unique_ptr<KeyValue>> KeyValue::create_inmemory() {
TRY_RESULT(res, detail::KeyValueInmemory::create());
return std::move(res);
}
} // namespace tonlib

18
tonlib/tonlib/KeyValue.h Normal file
View file

@ -0,0 +1,18 @@
#pragma once
#include "td/utils/SharedSlice.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
namespace tonlib {
class KeyValue {
public:
virtual ~KeyValue() = default;
virtual td::Status add(td::Slice key, td::Slice value) = 0;
virtual td::Status set(td::Slice key, td::Slice value) = 0;
virtual td::Status erase(td::Slice key) = 0;
virtual td::Result<td::SecureString> get(td::Slice key) = 0;
static td::Result<td::unique_ptr<KeyValue>> create_dir(td::CSlice dir);
static td::Result<td::unique_ptr<KeyValue>> create_inmemory();
};
} // namespace tonlib

View file

@ -25,22 +25,18 @@
namespace tonlib {
td::Status LastBlockStorage::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();
void LastBlockStorage::set_key_value(std::shared_ptr<KeyValue> kv) {
kv_ = std::move(kv);
}
std::string LastBlockStorage::get_file_name(td::Slice name) {
return directory_ + TD_DIR_SLASH + td::buffer_to_hex(name) + ".blkstate";
namespace {
std::string get_file_name(td::Slice name) {
return td::buffer_to_hex(name) + ".blkstate";
}
} // namespace
td::Result<LastBlockState> LastBlockStorage::get_state(td::Slice name) {
TRY_RESULT(data, td::read_file(get_file_name(name)));
TRY_RESULT(data, kv_->get(get_file_name(name)));
if (data.size() < 8) {
return td::Status::Error("too short");
}
@ -57,6 +53,6 @@ void LastBlockStorage::save_state(td::Slice name, LastBlockState state) {
std::string y(x.size() + 8, 0);
td::MutableSlice(y).substr(8).copy_from(x);
td::as<td::uint64>(td::MutableSlice(y).data()) = td::crc64(x);
td::atomic_write_file(get_file_name(name), y);
kv_->set(get_file_name(name), y);
}
} // namespace tonlib

View file

@ -20,15 +20,16 @@
#include "tonlib/LastBlock.h"
#include "tonlib/KeyValue.h"
namespace tonlib {
class LastBlockStorage {
public:
td::Status set_directory(std::string directory);
void set_key_value(std::shared_ptr<KeyValue> kv);
td::Result<LastBlockState> get_state(td::Slice name);
void save_state(td::Slice name, LastBlockState state);
private:
std::string directory_;
std::string get_file_name(td::Slice name);
std::shared_ptr<KeyValue> kv_;
};
} // namespace tonlib

View file

@ -34,7 +34,6 @@ vm::CellHash TestGiver::get_init_code_hash() {
td::Ref<vm::Cell> TestGiver::make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Slice message,
const block::StdAddress& dest_address) {
CHECK(message.size() <= 124);
td::BigInt256 dest_addr;
dest_addr.import_bits(dest_address.addr.as_bitslice());
vm::CellBuilder cb;
@ -45,7 +44,9 @@ td::Ref<vm::Cell> TestGiver::make_a_gift_message(td::uint32 seqno, td::uint64 gr
.store_int256(dest_addr, 256);
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms));
auto message_inner = cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4).store_bytes(message).finalize();
cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4);
vm::CellString::store(cb, message, 35 * 8).ensure();
auto message_inner = cb.finalize();
return vm::CellBuilder().store_long(seqno, 32).store_long(1, 8).store_ref(message_inner).finalize();
}
} // namespace tonlib

View file

@ -18,9 +18,11 @@
*/
#pragma once
#include "block/block.h"
#include "CellString.h"
namespace tonlib {
class TestGiver {
public:
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
static const block::StdAddress& address();
static vm::CellHash get_init_code_hash();
static td::Ref<vm::Cell> make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Slice message,

View file

@ -40,7 +40,6 @@ td::Ref<vm::Cell> TestWallet::get_init_message(const td::Ed25519::PrivateKey& pr
td::Ref<vm::Cell> TestWallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno,
td::int64 gramms, td::Slice message,
const block::StdAddress& dest_address) {
CHECK(message.size() <= 124);
td::BigInt256 dest_addr;
dest_addr.import_bits(dest_address.addr.as_bitslice());
vm::CellBuilder cb;
@ -50,7 +49,9 @@ td::Ref<vm::Cell> TestWallet::make_a_gift_message(const td::Ed25519::PrivateKey&
.store_long(dest_address.workchain, 8)
.store_int256(dest_addr, 256);
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms));
auto message_inner = cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4).store_bytes(message).finalize();
cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4);
vm::CellString::store(cb, message, 35 * 8).ensure();
auto message_inner = cb.finalize();
td::int8 send_mode = 3;
auto message_outer =
vm::CellBuilder().store_long(seqno, 32).store_long(send_mode, 8).store_ref(message_inner).finalize();

View file

@ -21,10 +21,12 @@
#include "vm/cells.h"
#include "Ed25519.h"
#include "block/block.h"
#include "CellString.h"
namespace tonlib {
class TestWallet {
public:
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
static td::Ref<vm::Cell> get_init_state(const td::Ed25519::PublicKey& public_key);
static td::Ref<vm::Cell> get_init_message(const td::Ed25519::PrivateKey& private_key);
static td::Ref<vm::Cell> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno,

View file

@ -547,8 +547,21 @@ td::Status TonlibClient::do_request(const tonlib_api::init& request,
if (!request.options_) {
return td::Status::Error(400, "Field options must not be empty");
}
TRY_STATUS(key_storage_.set_directory(request.options_->keystore_directory_));
TRY_STATUS(last_block_storage_.set_directory(request.options_->keystore_directory_));
if (!request.options_->keystore_type_) {
return td::Status::Error(400, "Field options.keystore_type must not be empty");
}
td::Result<td::unique_ptr<KeyValue>> r_kv;
downcast_call(
*request.options_->keystore_type_,
td::overloaded(
[&](tonlib_api::keyStoreTypeDirectory& directory) { r_kv = KeyValue::create_dir(directory.directory_); },
[&](tonlib_api::keyStoreTypeInMemory& inmemory) { r_kv = KeyValue::create_inmemory(); }));
TRY_RESULT(kv, std::move(r_kv));
kv_ = std::shared_ptr<KeyValue>(kv.release());
key_storage_.set_key_value(kv_);
last_block_storage_.set_key_value(kv_);
if (request.options_->config_) {
TRY_STATUS(set_config(std::move(request.options_->config_)));
}
@ -672,12 +685,11 @@ td::Result<tonlib_api::object_ptr<tonlib_api::raw_message>> to_raw_message_or_th
body = vm::load_cell_slice_ref(message.body->prefetch_ref());
}
std::string body_message;
if (body->size() % 8 == 0) {
body_message = std::string(body->size() / 8, 0);
body->prefetch_bytes(td::MutableSlice(body_message).ubegin(), body->size() / 8);
if (body_message.size() >= 4 && body_message[0] == 0 && body_message[1] == 0 && body_message[2] == 0 &&
body_message[3] == 0) {
body_message = body_message.substr(4);
if (body->size() >= 32 && body->prefetch_long(32) == 0) {
body.write().fetch_long(32);
auto r_body_message = vm::CellString::load(body.write());
if (r_body_message.is_ok()) {
body_message = r_body_message.move_as_ok();
}
}
@ -955,7 +967,7 @@ td::Status TonlibClient::do_request(const tonlib_api::testWallet_sendGrams& requ
if (!request.private_key_) {
return td::Status::Error(400, "Field private_key must not be empty");
}
if (request.message_.size() > 70) {
if (request.message_.size() > TestWallet::max_message_size) {
return td::Status::Error(400, "Message is too long");
}
TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_));
@ -1033,7 +1045,7 @@ td::Status TonlibClient::do_request(const tonlib_api::wallet_sendGrams& request,
if (!request.private_key_) {
return td::Status::Error(400, "Field private_key must not be empty");
}
if (request.message_.size() > 70) {
if (request.message_.size() > Wallet::max_message_size) {
return td::Status::Error(400, "Message is too long");
}
TRY_RESULT(valid_until, td::narrow_cast_safe<td::uint32>(request.valid_until_));
@ -1092,7 +1104,7 @@ td::Status TonlibClient::do_request(const tonlib_api::testGiver_sendGrams& reque
if (!request.destination_) {
return td::Status::Error(400, "Field destination must not be empty");
}
if (request.message_.size() > 70) {
if (request.message_.size() > TestGiver::max_message_size) {
return td::Status::Error(400, "Message is too long");
}
TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_));

View file

@ -24,6 +24,7 @@
#include "tonlib/ExtClient.h"
#include "tonlib/ExtClientOutbound.h"
#include "tonlib/KeyStorage.h"
#include "tonlib/KeyValue.h"
#include "tonlib/LastBlockStorage.h"
#include "td/actor/actor.h"
@ -55,6 +56,7 @@ class TonlibClient : public td::actor::Actor {
td::actor::ActorId<ExtClientOutbound> ext_client_outbound_;
// KeyStorage
std::shared_ptr<KeyValue> kv_;
KeyStorage key_storage_;
LastBlockStorage last_block_storage_;

View file

@ -17,6 +17,7 @@
Copyright 2017-2019 Telegram Systems LLP
*/
#include "tonlib/Wallet.h"
#include "tonlib/CellString.h"
#include "tonlib/GenericAccount.h"
#include "tonlib/utils.h"
@ -45,7 +46,6 @@ td::Ref<vm::Cell> Wallet::get_init_message(const td::Ed25519::PrivateKey& privat
td::Ref<vm::Cell> Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno,
td::uint32 valid_until, td::int64 gramms, td::Slice message,
const block::StdAddress& dest_address) {
CHECK(message.size() <= 124);
td::BigInt256 dest_addr;
dest_addr.import_bits(dest_address.addr.as_bitslice());
vm::CellBuilder cb;
@ -55,7 +55,9 @@ td::Ref<vm::Cell> Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& pri
.store_long(dest_address.workchain, 8)
.store_int256(dest_addr, 256);
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms));
auto message_inner = cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4).store_bytes(message).finalize();
cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4);
vm::CellString::store(cb, message, 35 * 8).ensure();
auto message_inner = cb.finalize();
td::int8 send_mode = 3;
auto message_outer = vm::CellBuilder()
.store_long(seqno, 32)

View file

@ -21,10 +21,12 @@
#include "vm/cells.h"
#include "Ed25519.h"
#include "block/block.h"
#include "CellString.h"
namespace tonlib {
class Wallet {
public:
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
static td::Ref<vm::Cell> get_init_state(const td::Ed25519::PublicKey& public_key);
static td::Ref<vm::Cell> get_init_message(const td::Ed25519::PrivateKey& private_key);
static td::Ref<vm::Cell> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno,

View file

@ -67,7 +67,8 @@ EncryptedKey DecryptedKey::encrypt(td::Slice local_password, td::Slice old_secre
}
td::SecureString encryption_secret(64);
pbkdf2_sha512(as_slice(decrypted_secret), "TON local key", PBKDF_ITERATIONS, encryption_secret.as_mutable_slice());
pbkdf2_sha512(as_slice(decrypted_secret), "TON local key", EncryptedKey::PBKDF_ITERATIONS,
encryption_secret.as_mutable_slice());
std::vector<td::SecureString> mnemonic_words_copy;
for (auto &w : mnemonic_words) {

View file

@ -25,9 +25,9 @@
#include <string>
namespace tonlib {
constexpr int PBKDF_ITERATIONS = 100000;
struct DecryptedKey;
struct EncryptedKey {
static constexpr int PBKDF_ITERATIONS = 100000;
td::SecureString encrypted_data;
td::Ed25519::PublicKey public_key;
td::SecureString secret;

View file

@ -77,7 +77,7 @@ td::Result<td::SecureString> SimpleEncryption::decrypt_data(td::Slice encrypted_
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");
return td::Status::Error("Failed to decrypt: data size is not divisible by 16");
}
auto data_hash = encrypted_data.substr(0, 32);
encrypted_data = encrypted_data.substr(32);

View file

@ -27,6 +27,7 @@ class TonlibCli : public td::actor::Actor {
std::string config;
std::string name;
std::string key_dir{"."};
bool in_memory{false};
bool use_callbacks_for_network{false};
bool use_simple_wallet{false};
bool ignore_cache{false};
@ -120,7 +121,14 @@ class TonlibCli : public td::actor::Actor {
? make_object<tonlib_api::config>(options_.config, options_.name,
options_.use_callbacks_for_network, options_.ignore_cache)
: nullptr;
send_query(make_object<tonlib_api::init>(make_object<tonlib_api::options>(std::move(config), options_.key_dir)),
tonlib_api::object_ptr<tonlib_api::KeyStoreType> ks_type;
if (options_.in_memory) {
ks_type = make_object<tonlib_api::keyStoreTypeInMemory>();
} else {
ks_type = make_object<tonlib_api::keyStoreTypeDirectory>(options_.key_dir);
}
send_query(make_object<tonlib_api::init>(make_object<tonlib_api::options>(std::move(config), std::move(ks_type))),
[](auto r_ok) {
LOG_IF(ERROR, r_ok.is_error()) << r_ok.error();
td::TerminalIO::out() << "Tonlib is inited\n";
@ -824,6 +832,10 @@ int main(int argc, char* argv[]) {
options.key_dir = arg.str();
return td::Status::OK();
});
p.add_option('M', "in-memory", "store keys only in-memory", [&]() {
options.in_memory = true;
return td::Status::OK();
});
p.add_option('E', "execute", "execute one command", [&](td::Slice arg) {
options.one_shot = true;
options.cmd = arg.str();