mirror of
https://github.com/danog/ton.git
synced 2024-11-26 12:04:48 +01:00
tonlib: big update
This commit is contained in:
parent
fd7a8de970
commit
ecb3e06a06
@ -588,7 +588,7 @@ void CatChainReceiverImpl::read_db() {
|
||||
read_db_ = true;
|
||||
|
||||
next_rotate_ = td::Timestamp::in(60 + td::Random::fast(0, 60));
|
||||
next_sync_ = td::Timestamp::in(0.01 * td::Random::fast(0, 60));
|
||||
next_sync_ = td::Timestamp::in(0.001 * td::Random::fast(0, 60));
|
||||
alarm_timestamp().relax(next_rotate_);
|
||||
alarm_timestamp().relax(next_sync_);
|
||||
|
||||
@ -627,7 +627,7 @@ void CatChainReceiverImpl::receive_query_from_overlay(adnl::AdnlNodeIdShort src,
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "db not read"));
|
||||
return;
|
||||
}
|
||||
td::PerfWarningTimer t{"catchain query process", 0.001};
|
||||
td::PerfWarningTimer t{"catchain query process", 0.05};
|
||||
auto F = fetch_tl_object<ton_api::Function>(data.clone(), true);
|
||||
if (F.is_error()) {
|
||||
callback_->on_custom_query(get_source_by_adnl_id(src)->get_hash(), std::move(data), std::move(promise));
|
||||
@ -909,7 +909,7 @@ void CatChainReceiverImpl::choose_neighbours() {
|
||||
void CatChainReceiverImpl::alarm() {
|
||||
alarm_timestamp() = td::Timestamp::never();
|
||||
if (next_sync_ && next_sync_.is_in_past()) {
|
||||
next_sync_ = td::Timestamp::in(td::Random::fast(2.0, 3.0));
|
||||
next_sync_ = td::Timestamp::in(td::Random::fast(0.1, 0.2));
|
||||
for (auto i = 0; i < 3; i++) {
|
||||
auto S = get_source(td::Random::fast(0, get_sources_cnt() - 1));
|
||||
CHECK(S != nullptr);
|
||||
|
@ -315,6 +315,8 @@ if (NOT CMAKE_CROSSCOMPILING)
|
||||
GenFif(DEST smartcont/config-code.fif SOURCE smartcont/config-code.fc)
|
||||
GenFif(DEST smartcont/wallet-code.fif SOURCE smartcont/wallet-code.fc)
|
||||
GenFif(DEST smartcont/simple-wallet-code.fif SOURCE smartcont/simple-wallet-code.fc)
|
||||
GenFif(DEST smartcont/highload-wallet-code.fif SOURCE smartcont/highload-wallet-code.fc)
|
||||
GenFif(DEST smartcont/highload-wallet-v2-code.fif SOURCE smartcont/highload-wallet-v2-code.fc)
|
||||
GenFif(DEST smartcont/elector-code.fif SOURCE smartcont/elector-code.fc)
|
||||
endif()
|
||||
|
||||
@ -333,3 +335,6 @@ target_link_libraries(dump-block PUBLIC ton_crypto fift-lib ton_block)
|
||||
if (WINGETOPT_FOUND)
|
||||
target_link_libraries_system(dump-block wingetopt)
|
||||
endif()
|
||||
|
||||
install(TARGETS fift func RUNTIME DESTINATION bin)
|
||||
install(DIRECTORY fift/lib/ DESTINATION lib/fift)
|
||||
|
@ -169,6 +169,11 @@ bool SourceReader::load_line() {
|
||||
error("line too long");
|
||||
return false;
|
||||
}
|
||||
if (len && cur_line.back() == '\r') {
|
||||
// CP/M line breaks support
|
||||
cur_line.pop_back();
|
||||
--len;
|
||||
}
|
||||
loc.text = cur_line;
|
||||
cur_line_len = (int)len;
|
||||
loc.line_pos = 0;
|
||||
|
41
crypto/smartcont/highload-wallet-code.fc
Normal file
41
crypto/smartcont/highload-wallet-code.fc
Normal file
@ -0,0 +1,41 @@
|
||||
;; Heavy-duty wallet for mass transfers (e.g., for cryptocurrency exchanges)
|
||||
;; accepts orders for up to 254 internal messages (transfers) in one external message
|
||||
|
||||
() recv_internal(slice in_msg) impure {
|
||||
;; do nothing for internal messages
|
||||
}
|
||||
|
||||
() recv_external(slice in_msg) impure {
|
||||
var signature = in_msg~load_bits(512);
|
||||
var cs = in_msg;
|
||||
var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32));
|
||||
throw_if(35, valid_until <= now());
|
||||
var ds = get_data().begin_parse();
|
||||
var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256));
|
||||
ds.end_parse();
|
||||
throw_unless(33, msg_seqno == stored_seqno);
|
||||
throw_unless(34, subwallet_id == stored_subwallet);
|
||||
throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key));
|
||||
var dict = cs~load_dict();
|
||||
cs.end_parse();
|
||||
accept_message();
|
||||
int i = -1;
|
||||
do {
|
||||
(i, var cs, var f) = dict.idict_get_next?(16, i);
|
||||
if (f) {
|
||||
var mode = cs~load_uint(8);
|
||||
send_raw_message(cs~load_ref(), mode);
|
||||
}
|
||||
} until (~ f);
|
||||
set_data(begin_cell()
|
||||
.store_uint(stored_seqno + 1, 32)
|
||||
.store_uint(stored_subwallet, 32)
|
||||
.store_uint(public_key, 256)
|
||||
.end_cell());
|
||||
}
|
||||
|
||||
;; Get methods
|
||||
|
||||
int seqno() method_id {
|
||||
return get_data().begin_parse().preload_uint(32);
|
||||
}
|
65
crypto/smartcont/highload-wallet-v2-code.fc
Normal file
65
crypto/smartcont/highload-wallet-v2-code.fc
Normal file
@ -0,0 +1,65 @@
|
||||
;; Heavy-duty wallet for mass transfers (e.g., for cryptocurrency exchanges)
|
||||
;; accepts orders for up to 254 internal messages (transfers) in one external message
|
||||
;; this version does not use seqno for replay protection; instead, it remembers all recent query_ids
|
||||
;; in this way several external messages with different query_id can be sent in parallel
|
||||
|
||||
() recv_internal(slice in_msg) impure {
|
||||
;; do nothing for internal messages
|
||||
}
|
||||
|
||||
() recv_external(slice in_msg) impure {
|
||||
var signature = in_msg~load_bits(512);
|
||||
var cs = in_msg;
|
||||
var (subwallet_id, query_id) = (cs~load_uint(32), cs~load_uint(64));
|
||||
var bound = (now() << 32);
|
||||
throw_if(35, query_id < bound);
|
||||
var ds = get_data().begin_parse();
|
||||
var (stored_subwallet, last_cleaned, public_key, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict());
|
||||
ds.end_parse();
|
||||
(_, var found?) = old_queries.udict_get?(64, query_id);
|
||||
throw_if(32, found?);
|
||||
throw_unless(34, subwallet_id == stored_subwallet);
|
||||
throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key));
|
||||
var dict = cs~load_dict();
|
||||
cs.end_parse();
|
||||
accept_message();
|
||||
int i = -1;
|
||||
do {
|
||||
(i, var cs, var f) = dict.idict_get_next?(16, i);
|
||||
if (f) {
|
||||
var mode = cs~load_uint(8);
|
||||
send_raw_message(cs~load_ref(), mode);
|
||||
}
|
||||
} until (~ f);
|
||||
bound -= (64 << 32); ;; clean up records expired more than 64 seconds ago
|
||||
old_queries~udict_set_builder(64, query_id, begin_cell());
|
||||
var queries = old_queries;
|
||||
do {
|
||||
var (old_queries', i, _, f) = old_queries.udict_delete_get_min(64);
|
||||
f~touch();
|
||||
if (f) {
|
||||
f = (i < bound);
|
||||
}
|
||||
if (f) {
|
||||
old_queries = old_queries';
|
||||
last_cleaned = i;
|
||||
}
|
||||
} until (~ f);
|
||||
set_data(begin_cell()
|
||||
.store_uint(stored_subwallet, 32)
|
||||
.store_uint(last_cleaned, 64)
|
||||
.store_uint(public_key, 256)
|
||||
.store_dict(old_queries)
|
||||
.end_cell());
|
||||
}
|
||||
|
||||
;; Get methods
|
||||
|
||||
;; returns -1 for processed queries, 0 for unprocessed, 1 for unknown (forgotten)
|
||||
int processed?(int query_id) method_id {
|
||||
var ds = get_data().begin_parse();
|
||||
var (_, last_cleaned, _, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict());
|
||||
ds.end_parse();
|
||||
(_, var found) = old_queries.udict_get?(64, query_id);
|
||||
return found ? true : - (query_id <= last_cleaned);
|
||||
}
|
68
crypto/smartcont/highload-wallet.fif
Normal file
68
crypto/smartcont/highload-wallet.fif
Normal file
@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <filename-base> <subwallet-id> <seqno> <order-file> [<savefile>]" cr
|
||||
."Creates a request with up to 254 orders loaded from <order-file> to high-load (sub)wallet created by new-highload-wallet.fif, with private key loaded from file <filename-base>.pk "
|
||||
."and address from <filename-base><subwallet-id>.addr, and saves it into <savefile>.boc ('wallet-query.boc' by default)" cr
|
||||
."<order-file> is a text file with lines `SEND <dest-addr> <amount>`" cr 1 halt
|
||||
} : usage
|
||||
$# dup 4 < swap 5 > or ' usage if
|
||||
|
||||
$1 =: file-base
|
||||
$2 parse-int dup 32 fits ' usage ifnot =: subwallet-id // parse subwallet-id
|
||||
{ subwallet-id (.) $+ } : +subwallet
|
||||
$3 parse-int =: seqno
|
||||
$4 =: order-file
|
||||
def? $5 { @' $5 } { "wallet-query" } cond constant savefile
|
||||
3 constant send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors
|
||||
60 constant timeout // external message expires in 60 seconds
|
||||
|
||||
file-base +subwallet +".addr" load-address
|
||||
2dup 2constant wallet_addr
|
||||
."Source wallet address = " 2dup .addr cr 6 .Addr cr
|
||||
file-base +".pk" load-keypair nip constant wallet_pk
|
||||
|
||||
variable orders dictnew orders !
|
||||
variable order# order# 0!
|
||||
// c --
|
||||
{ <s order# @ dup 254 >= abort"more than 254 orders"
|
||||
orders @ 16 udict!+ not abort"cannot add order to dictionary"
|
||||
orders ! order# 1+!
|
||||
} : add-order
|
||||
// b body -- b'
|
||||
{ tuck <s 2dup s-fits? not rot over 1 i, -rot
|
||||
{ drop swap ref, } { s, nip } cond
|
||||
} : append-msg-body
|
||||
// ng wc addr bounce body -- c
|
||||
{ <b b{01} s, rot 1 i, b{000100} s, 2swap addr, rot Gram,
|
||||
0 9 64 32 + + 1+ u, swap append-msg-body b>
|
||||
} : create-int-msg
|
||||
// ng wc addr bnc --
|
||||
{ ."Transferring " 3 roll .GR ."to account "
|
||||
-rot 2dup 4 pick 7 + .Addr ." = " .addr ." bounce=" . cr
|
||||
} : .transfer
|
||||
// addr$ ng -- c
|
||||
{ swap parse-smc-addr // ng wc addr bnc
|
||||
2over 2over .transfer
|
||||
<b 0 32 u, b> create-int-msg
|
||||
} : create-simple-transfer
|
||||
// c m -- c'
|
||||
{ <b swap 8 u, swap ref, b> } : create-order
|
||||
|
||||
// addr$ ng --
|
||||
{ create-simple-transfer send-mode create-order add-order } : send
|
||||
{ bl word bl word $>GR send } : SEND
|
||||
|
||||
// parse order file
|
||||
order-file include
|
||||
|
||||
// create external message
|
||||
<b subwallet-id 32 i, now timeout + 32 u, seqno 32 u, orders @ dict, b>
|
||||
dup ."signing message: " <s csr. cr
|
||||
dup hash wallet_pk ed25519_sign_uint
|
||||
<b b{1000100} s, wallet_addr addr, 0 Gram, b{00} s,
|
||||
swap B, swap <s s, b>
|
||||
dup ."resulting external message: " <s csr. cr
|
||||
2 boc+>B dup Bx. cr
|
||||
savefile +".boc" tuck B>file
|
||||
."(Saved to file " type .")" cr
|
44
crypto/smartcont/new-highload-wallet.fif
Normal file
44
crypto/smartcont/new-highload-wallet.fif
Normal file
@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env fift -s
|
||||
"TonUtil.fif" include
|
||||
"Asm.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <workchain-id> <subwallet-id> [<filename-base>]" cr
|
||||
."Creates a new high-load wallet in the specified workchain, with the controlling private key saved to or loaded from <filename-base>.pk "
|
||||
."('new-wallet.pk' by default)" cr
|
||||
."<subwallet-id> is the 32-bit identifier of this subwallet among all controlled by the same private key" cr 1 halt
|
||||
} : usage
|
||||
$# 2- -2 and ' usage if
|
||||
|
||||
$1 parse-workchain-id =: wc // set workchain id from command line argument
|
||||
$2 parse-int dup =: subwallet-id // parse subwallet-id
|
||||
32 fits ' usage ifnot
|
||||
{ subwallet-id (.) $+ } : +subwallet
|
||||
def? $3 { @' $3 } { "new-wallet" } cond constant file-base
|
||||
|
||||
."Creating new high-load wallet in workchain " wc .
|
||||
."with subwallet id " subwallet-id . cr
|
||||
|
||||
// Create new high-load wallet; source code included from `highload-wallet-code.fif`
|
||||
"highload-wallet-code.fif" include
|
||||
// code
|
||||
<b 0 32 u, subwallet-id 32 i,
|
||||
file-base +".pk" load-generate-keypair
|
||||
constant wallet_pk
|
||||
B,
|
||||
b> // data
|
||||
null // no libraries
|
||||
<b b{0011} s, 3 roll ref, rot ref, swap dict, b> // create StateInit
|
||||
dup ."StateInit: " <s csr. cr
|
||||
dup hash wc swap 2dup 2constant wallet_addr
|
||||
."new wallet address = " 2dup .addr cr
|
||||
2dup file-base +subwallet +".addr" save-address-verbose
|
||||
."Non-bounceable address (for init): " 2dup 7 .Addr cr
|
||||
."Bounceable address (for later access): " 6 .Addr cr
|
||||
<b subwallet-id 32 i, -1 32 i, 0 32 u, false 1 i, b>
|
||||
dup ."signing message: " <s csr. cr
|
||||
dup hash wallet_pk ed25519_sign_uint rot
|
||||
<b b{1000100} s, wallet_addr addr, b{000010} s, swap <s s, b{0} s, swap B, swap <s s, b>
|
||||
dup ."External message for initialization is " <s csr. cr
|
||||
2 boc+>B dup Bx. cr
|
||||
file-base +subwallet +"-query.boc" tuck B>file
|
||||
."(Saved wallet creating query to file " type .")" cr
|
@ -86,6 +86,8 @@ cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(va
|
||||
(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
|
||||
(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
|
||||
(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
|
||||
(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
|
||||
(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
|
||||
cell new_dict() asm "NEWDICT";
|
||||
int dict_empty?(cell c) asm "DICTEMPTY";
|
||||
|
||||
|
@ -16,8 +16,12 @@ vector {t:Type} # [ t ] = Vector t;
|
||||
error code:int32 message:string = Error;
|
||||
ok = Ok;
|
||||
|
||||
keyStoreTypeDirectory directory:string = KeyStoreType;
|
||||
keyStoreTypeInMemory = KeyStoreType;
|
||||
|
||||
config config:string blockchain_name:string use_callbacks_for_network:Bool ignore_cache:Bool = Config;
|
||||
options config:config keystore_directory:string = Options;
|
||||
|
||||
options config:config keystore_type:KeyStoreType = Options;
|
||||
|
||||
key public_key:string secret:secureBytes = Key;
|
||||
inputKey key:key local_password:secureBytes = InputKey;
|
||||
@ -37,7 +41,7 @@ raw.initialAccountState code:bytes data:bytes = raw.InitialAccountState;
|
||||
raw.accountState balance:int64 code:bytes data:bytes last_transaction_id:internal.transactionId sync_utime:int53 = raw.AccountState;
|
||||
raw.message source:string destination:string value:int64 message:bytes = raw.Message;
|
||||
raw.transaction utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 in_msg:raw.message out_msgs:vector<raw.message> = raw.Transaction;
|
||||
raw.transactions transactions:vector<raw.Transaction> previous_transaction_id:internal.transactionId = raw.Transactions;
|
||||
raw.transactions transactions:vector<raw.transaction> previous_transaction_id:internal.transactionId = raw.Transactions;
|
||||
|
||||
testWallet.initialAccountState public_key:string = testWallet.InitialAccountState;
|
||||
testWallet.accountState balance:int64 seqno:int32 last_transaction_id:internal.transactionId sync_utime:int53 = testWallet.AccountState;
|
||||
|
Binary file not shown.
@ -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
|
||||
|
3
tonlib/TonlibConfig.cmake
Normal file
3
tonlib/TonlibConfig.cmake
Normal file
@ -0,0 +1,3 @@
|
||||
include(CMakeFindDependencyMacro)
|
||||
#TODO: write all external dependencies
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/TonlibTargets.cmake")
|
@ -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");
|
||||
{
|
||||
|
@ -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>(
|
||||
|
64
tonlib/tonlib/CellString.cpp
Normal file
64
tonlib/tonlib/CellString.cpp
Normal 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
|
22
tonlib/tonlib/CellString.h
Normal file
22
tonlib/tonlib/CellString.h
Normal 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
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
103
tonlib/tonlib/KeyValue.cpp
Normal 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
18
tonlib/tonlib/KeyValue.h
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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_));
|
||||
|
@ -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_;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -5,3 +5,5 @@ add_executable (validator-engine-console validator-engine-console.cpp
|
||||
validator-engine-console-query.h )
|
||||
target_link_libraries(validator-engine-console tdutils tdactor adnllite tl_api tl_lite_api tl-lite-utils ton_crypto ton_block terminal)
|
||||
|
||||
install(TARGETS validator-engine-console RUNTIME DESTINATION bin)
|
||||
|
||||
|
@ -14,3 +14,5 @@ add_executable(validator-engine ${VALIDATOR_ENGINE_SOURCE})
|
||||
target_link_libraries(validator-engine overlay tdutils tdactor adnl tl_api dht
|
||||
rldp catchain validatorsession full-node validator ton_validator validator
|
||||
fift-lib memprof ${JEMALLOC_LIBRARIES})
|
||||
|
||||
install(TARGETS validator-engine RUNTIME DESTINATION bin)
|
||||
|
@ -55,7 +55,7 @@ ValidatorSessionDescriptionImpl::ValidatorSessionDescriptionImpl(ValidatorSessio
|
||||
pdata_temp_size_ = 1 << 30;
|
||||
pdata_temp_ = new td::uint8[pdata_temp_size_];
|
||||
|
||||
pdata_perm_size_ = 1 << 30;
|
||||
pdata_perm_size_ = 1ull << 30;
|
||||
pdata_perm_ptr_ = 0;
|
||||
|
||||
for (auto &el : cache_) {
|
||||
|
Loading…
Reference in New Issue
Block a user