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

updated smartcontracts

- updated smartcontracts
- updated fullnode database layout
- fixed memory leak in blockchain-explorer
- updated tonlib
This commit is contained in:
ton 2019-10-23 17:43:50 +04:00
parent 9c9248a9ae
commit c860ce3d1e
104 changed files with 7309 additions and 1335 deletions

View file

@ -9,7 +9,7 @@ get_filename_component(TON_REAL_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" REALPAT
get_filename_component(TON_REAL_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}" REALPATH) get_filename_component(TON_REAL_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}" REALPATH)
if (TON_REAL_BINARY_DIR STREQUAL TON_REAL_SOURCE_DIR) if (TON_REAL_BINARY_DIR STREQUAL TON_REAL_SOURCE_DIR)
message(" Out-of-source build should be used to build TDLib.") message(" Out-of-source build should be used to build TON.")
message(" You need to remove the files already created by CMake and") message(" You need to remove the files already created by CMake and")
message(" rerun CMake from a new directory:") message(" rerun CMake from a new directory:")
message(" rm -rf CMakeFiles CMakeCache.txt") message(" rm -rf CMakeFiles CMakeCache.txt")
@ -190,6 +190,7 @@ endif()
set(CMAKE_THREAD_PREFER_PTHREAD ON) set(CMAKE_THREAD_PREFER_PTHREAD ON)
set(THREADS_PREFER_PTHREAD_FLAG ON) set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
find_package(ZLIB REQUIRED)
if (TON_ARCH AND NOT MSVC) if (TON_ARCH AND NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${TON_ARCH}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${TON_ARCH}")
@ -380,7 +381,7 @@ if (NOT CMAKE_CROSSCOMPILING)
if (TDUTILS_MIME_TYPE) if (TDUTILS_MIME_TYPE)
set(TDMIME_AUTO tdmime_auto) set(TDMIME_AUTO tdmime_auto)
endif() endif()
add_custom_target(prepare_cross_compiling DEPENDS tl_generate_common tlb_generate_block ${TDMIME_AUTO}) add_custom_target(prepare_cross_compiling DEPENDS tl_generate_common tlb_generate_block gen_fif ${TDMIME_AUTO})
endif() endif()
#TESTS #TESTS
@ -390,6 +391,9 @@ target_link_libraries(test-ed25519 PRIVATE ton_crypto)
add_executable(test-vm test/test-td-main.cpp ${TONVM_TEST_SOURCE}) add_executable(test-vm test/test-td-main.cpp ${TONVM_TEST_SOURCE})
target_link_libraries(test-vm PRIVATE ton_crypto fift-lib) target_link_libraries(test-vm PRIVATE ton_crypto fift-lib)
add_executable(test-smartcont test/test-td-main.cpp ${SMARTCONT_TEST_SOURCE})
target_link_libraries(test-smartcont PRIVATE smc-envelope fift-lib ton_db)
add_executable(test-cells test/test-td-main.cpp ${CELLS_TEST_SOURCE}) add_executable(test-cells test/test-td-main.cpp ${CELLS_TEST_SOURCE})
target_link_libraries(test-cells PRIVATE ton_crypto) target_link_libraries(test-cells PRIVATE ton_crypto)
@ -490,10 +494,12 @@ endif()
enable_testing() enable_testing()
set(TEST_OPTIONS "--regression ${CMAKE_CURRENT_SOURCE_DIR}/test/regression-tests.ans --filter -Bench") set(TEST_OPTIONS "--regression ${CMAKE_CURRENT_SOURCE_DIR}/test/regression-tests.ans --filter -Bench")
separate_arguments(TEST_OPTIONS) separate_arguments(TEST_OPTIONS)
add_test(test-ed25519-crypto crypto/test-ed25519-crypto)
add_test(test-ed25519 test-ed25519) add_test(test-ed25519 test-ed25519)
add_test(test-vm test-vm ${TEST_OPTIONS}) add_test(test-vm test-vm ${TEST_OPTIONS})
add_test(test-fift test-fift ${TEST_OPTIONS}) add_test(test-fift test-fift ${TEST_OPTIONS})
add_test(test-cells test-cells ${TEST_OPTIONS}) add_test(test-cells test-cells ${TEST_OPTIONS})
add_test(test-smartcont test-smartcont)
add_test(test-net test-net) add_test(test-net test-net)
add_test(test-actors test-tdactor) add_test(test-actors test-tdactor)

View file

@ -146,8 +146,8 @@ void AdnlExtServerImpl::add_tcp_port(td::uint16 port) {
} }
}; };
auto act = td::actor::create_actor<TcpInfiniteListener>(td::actor::ActorOptions().with_name("listener").with_poll(), auto act = td::actor::create_actor<td::TcpInfiniteListener>(
port, std::make_unique<Callback>(actor_id(this))); td::actor::ActorOptions().with_name("listener").with_poll(), port, std::make_unique<Callback>(actor_id(this)));
listeners_.emplace(port, std::move(act)); listeners_.emplace(port, std::move(act));
} }

View file

@ -32,69 +32,6 @@ namespace ton {
namespace adnl { namespace adnl {
class TcpInfiniteListener : public td::actor::Actor {
public:
TcpInfiniteListener(td::int32 port, std::unique_ptr<td::TcpListener::Callback> callback)
: port_(port), callback_(std::move(callback)) {
}
private:
td::int32 port_;
std::unique_ptr<td::TcpListener::Callback> callback_;
td::actor::ActorOwn<td::TcpListener> tcp_listener_;
td::int32 refcnt_{0};
bool close_flag_{false};
void start_up() override {
loop();
}
void hangup() override {
close_flag_ = true;
tcp_listener_.reset();
if (refcnt_ == 0) {
stop();
}
}
void loop() override {
if (!tcp_listener_.empty()) {
return;
}
class Callback : public td::TcpListener::Callback {
public:
Callback(td::actor::ActorShared<TcpInfiniteListener> parent) : parent_(std::move(parent)) {
}
void accept(td::SocketFd fd) override {
td::actor::send_closure(parent_, &TcpInfiniteListener::accept, std::move(fd));
}
private:
td::actor::ActorShared<TcpInfiniteListener> parent_;
};
refcnt_++;
tcp_listener_ = td::actor::create_actor<td::TcpListener>(
td::actor::ActorOptions().with_name(PSLICE() << "TcpListener" << td::tag("port", port_)).with_poll(), port_,
std::make_unique<Callback>(actor_shared(this)));
}
void accept(td::SocketFd fd) {
callback_->accept(std::move(fd));
}
void hangup_shared() override {
refcnt_--;
tcp_listener_.reset();
if (close_flag_) {
if (refcnt_ == 0) {
stop();
}
} else {
alarm_timestamp() = td::Timestamp::in(5 /*5 seconds*/);
}
}
};
class AdnlExtServerImpl; class AdnlExtServerImpl;
class AdnlInboundConnection : public AdnlExtConnection { class AdnlInboundConnection : public AdnlExtConnection {
@ -150,7 +87,7 @@ class AdnlExtServerImpl : public AdnlExtServer {
td::actor::ActorId<AdnlPeerTable> peer_table_; td::actor::ActorId<AdnlPeerTable> peer_table_;
std::set<AdnlNodeIdShort> local_ids_; std::set<AdnlNodeIdShort> local_ids_;
std::set<td::uint16> ports_; std::set<td::uint16> ports_;
std::map<td::uint16, td::actor::ActorOwn<TcpInfiniteListener>> listeners_; std::map<td::uint16, td::actor::ActorOwn<td::TcpInfiniteListener>> listeners_;
}; };
} // namespace adnl } // namespace adnl

View file

@ -393,8 +393,8 @@ class CoreActor : public CoreActorInterface {
clients_.emplace_back(ton::adnl::AdnlExtClient::create(ton::adnl::AdnlNodeIdFull{remote_public_key_}, clients_.emplace_back(ton::adnl::AdnlExtClient::create(ton::adnl::AdnlNodeIdFull{remote_public_key_},
remote_addr_, make_callback(0))); remote_addr_, make_callback(0)));
} }
daemon_ = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, static_cast<td::uint16>(http_port_), nullptr, nullptr, daemon_ = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, static_cast<td::uint16>(http_port_), nullptr, nullptr,
&process_http_request, nullptr, MHD_OPTION_END); &process_http_request, nullptr, MHD_OPTION_THREAD_POOL_SIZE, 16, MHD_OPTION_END);
CHECK(daemon_ != nullptr); CHECK(daemon_ != nullptr);
} }
}; };

24
common/int-to-string.hpp Normal file
View file

@ -0,0 +1,24 @@
#pragma once
#include "td/utils/int_types.h"
#include "td/utils/Slice.h"
namespace ton {
template <typename T>
typename std::enable_if_t<std::is_integral<T>::value, td::MutableSlice> store_int_to_slice(td::MutableSlice S,
const T &v) {
CHECK(S.size() >= sizeof(T));
S.copy_from(td::Slice(reinterpret_cast<const td::uint8 *>(&v), sizeof(T)));
return S.remove_prefix(sizeof(T));
}
template <typename T>
typename std::enable_if_t<std::is_integral<T>::value, T> fetch_int_from_slice(td::Slice S) {
CHECK(S.size() >= sizeof(T));
T v;
td::MutableSlice(reinterpret_cast<td::uint8 *>(&v), sizeof(T)).copy_from(S.truncate(sizeof(T)));
return v;
}
} // namespace ton

View file

@ -88,6 +88,7 @@ set(TON_CRYPTO_SOURCE
vm/cells/CellBuilder.cpp vm/cells/CellBuilder.cpp
vm/cells/CellHash.cpp vm/cells/CellHash.cpp
vm/cells/CellSlice.cpp vm/cells/CellSlice.cpp
vm/cells/CellString.cpp
vm/cells/CellTraits.cpp vm/cells/CellTraits.cpp
vm/cells/CellUsageTree.cpp vm/cells/CellUsageTree.cpp
vm/cells/DataCell.cpp vm/cells/DataCell.cpp
@ -99,6 +100,7 @@ set(TON_CRYPTO_SOURCE
vm/cells/CellBuilder.h vm/cells/CellBuilder.h
vm/cells/CellHash.h vm/cells/CellHash.h
vm/cells/CellSlice.h vm/cells/CellSlice.h
vm/cells/CellString.h
vm/cells/CellTraits.h vm/cells/CellTraits.h
vm/cells/CellUsageTree.h vm/cells/CellUsageTree.h
vm/cells/CellWithStorage.h vm/cells/CellWithStorage.h
@ -197,6 +199,24 @@ set(BLOCK_SOURCE
block/transaction.h block/transaction.h
) )
set(SMC_ENVELOPE_SOURCE
smc-envelope/GenericAccount.cpp
smc-envelope/MultisigWallet.cpp
smc-envelope/SmartContract.cpp
smc-envelope/SmartContractCode.cpp
smc-envelope/TestGiver.cpp
smc-envelope/TestWallet.cpp
smc-envelope/Wallet.cpp
smc-envelope/GenericAccount.h
smc-envelope/MultisigWallet.h
smc-envelope/SmartContract.h
smc-envelope/SmartContractCode.h
smc-envelope/TestGiver.h
smc-envelope/TestWallet.h
smc-envelope/Wallet.h
)
set(ED25519_TEST_SOURCE set(ED25519_TEST_SOURCE
${CMAKE_CURRENT_SOURCE_DIR}/test/Ed25519.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test/Ed25519.cpp
PARENT_SCOPE PARENT_SCOPE
@ -217,6 +237,11 @@ set(TONVM_TEST_SOURCE
PARENT_SCOPE PARENT_SCOPE
) )
set(SMARTCONT_TEST_SOURCE
${CMAKE_CURRENT_SOURCE_DIR}/test/test-smartcont.cpp
PARENT_SCOPE
)
set(FIFT_TEST_SOURCE set(FIFT_TEST_SOURCE
${CMAKE_CURRENT_SOURCE_DIR}/test/fift.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test/fift.cpp
PARENT_SCOPE PARENT_SCOPE
@ -329,10 +354,18 @@ if (NOT CMAKE_CROSSCOMPILING)
GenFif(DEST smartcont/auto/highload-wallet-code SOURCE smartcont/highload-wallet-code.fc NAME highload-wallet) GenFif(DEST smartcont/auto/highload-wallet-code SOURCE smartcont/highload-wallet-code.fc NAME highload-wallet)
GenFif(DEST smartcont/auto/highload-wallet-v2-code SOURCE smartcont/highload-wallet-v2-code.fc NAME highoad-wallet-v2) GenFif(DEST smartcont/auto/highload-wallet-v2-code SOURCE smartcont/highload-wallet-v2-code.fc NAME highoad-wallet-v2)
GenFif(DEST smartcont/auto/elector-code SOURCE smartcont/elector-code.fc NAME elector-code) GenFif(DEST smartcont/auto/elector-code SOURCE smartcont/elector-code.fc NAME elector-code)
GenFif(DEST smartcont/auto/multisig-code SOURCE smartcont/multisig-code.fc NAME multisig)
GenFif(DEST smartcont/auto/restricted-wallet-code SOURCE smartcont/restricted-wallet-code.fc NAME restricted-wallet) GenFif(DEST smartcont/auto/restricted-wallet-code SOURCE smartcont/restricted-wallet-code.fc NAME restricted-wallet)
GenFif(DEST smartcont/auto/restricted-wallet2-code SOURCE smartcont/restricted-wallet2-code.fc NAME restricted-wallet2) GenFif(DEST smartcont/auto/restricted-wallet2-code SOURCE smartcont/restricted-wallet2-code.fc NAME restricted-wallet2)
GenFif(DEST smartcont/auto/simple-wallet-ext-code SOURCE smartcont/simple-wallet-ext-code.fc NAME simple-wallet-ext)
endif() endif()
add_library(smc-envelope ${SMC_ENVELOPE_SOURCE})
target_include_directories(smc-envelope PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_link_libraries(smc-envelope PUBLIC ton_crypto PRIVATE tdutils ton_block)
add_dependencies(smc-envelope gen_fif)
add_executable(create-state block/create-state.cpp) add_executable(create-state block/create-state.cpp)
target_include_directories(create-state PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> target_include_directories(create-state PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>) $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)

View file

@ -551,6 +551,9 @@ validator_addr#73 public_key:SigPubKey weight:uint64 adnl_addr:bits256 = Validat
validators#11 utime_since:uint32 utime_until:uint32 validators#11 utime_since:uint32 utime_until:uint32
total:(## 16) main:(## 16) { main <= total } { main >= 1 } total:(## 16) main:(## 16) { main <= total } { main >= 1 }
list:(Hashmap 16 ValidatorDescr) = ValidatorSet; list:(Hashmap 16 ValidatorDescr) = ValidatorSet;
validators_ext#12 utime_since:uint32 utime_until:uint32
total:(## 16) main:(## 16) { main <= total } { main >= 1 }
total_weight:uint64 list:(HashmapE 16 ValidatorDescr) = ValidatorSet;
_ config_addr:bits256 = ConfigParam 0; _ config_addr:bits256 = ConfigParam 0;
_ elector_addr:bits256 = ConfigParam 1; _ elector_addr:bits256 = ConfigParam 1;

View file

@ -221,8 +221,6 @@ td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const
td::Result<AccountState::Info> AccountState::validate(ton::BlockIdExt ref_blk, block::StdAddress addr) const { td::Result<AccountState::Info> AccountState::validate(ton::BlockIdExt ref_blk, block::StdAddress addr) const {
TRY_RESULT_PREFIX(root, vm::std_boc_deserialize(state.as_slice(), true), "cannot deserialize account state"); TRY_RESULT_PREFIX(root, vm::std_boc_deserialize(state.as_slice(), true), "cannot deserialize account state");
LOG(INFO) << "got account state for " << addr << " with respect to blocks " << blk.to_str()
<< (shard_blk == blk ? "" : std::string{" and "} + shard_blk.to_str());
if (blk != ref_blk && ref_blk.id.seqno != ~0U) { if (blk != ref_blk && ref_blk.id.seqno != ~0U) {
return td::Status::Error(PSLICE() << "obtained getAccountState() for a different reference block " << blk.to_str() return td::Status::Error(PSLICE() << "obtained getAccountState() for a different reference block " << blk.to_str()
<< " instead of requested " << ref_blk.to_str()); << " instead of requested " << ref_blk.to_str());

View file

@ -387,11 +387,25 @@ td::Result<std::unique_ptr<ValidatorSet>> Config::unpack_validator_set(Ref<vm::C
if (vset_root.is_null()) { if (vset_root.is_null()) {
return td::Status::Error("validator set is absent"); return td::Status::Error("validator set is absent");
} }
gen::ValidatorSet::Record rec; gen::ValidatorSet::Record_validators_ext rec;
if (!tlb::unpack_cell(std::move(vset_root), rec)) { Ref<vm::Cell> dict_root;
return td::Status::Error("validator set is invalid"); if (!tlb::unpack_cell(vset_root, rec)) {
gen::ValidatorSet::Record_validators rec0;
if (!tlb::unpack_cell(std::move(vset_root), rec0)) {
return td::Status::Error("validator set is invalid");
}
rec.utime_since = rec0.utime_since;
rec.utime_until = rec0.utime_until;
rec.total = rec0.total;
rec.main = rec0.main;
dict_root = vm::Dictionary::construct_root_from(*rec0.list);
rec.total_weight = 0;
} else if (rec.total_weight) {
dict_root = rec.list->prefetch_ref();
} else {
return td::Status::Error("validator set cannot have zero total weight");
} }
vm::Dictionary dict{vm::Dictionary::construct_root_from(*rec.list), 16}; vm::Dictionary dict{std::move(dict_root), 16};
td::BitArray<16> key_buffer; td::BitArray<16> key_buffer;
auto last = dict.get_minmax_key(key_buffer.bits(), 16, true); auto last = dict.get_minmax_key(key_buffer.bits(), 16, true);
if (last.is_null() || (int)key_buffer.to_ulong() != rec.total - 1) { if (last.is_null() || (int)key_buffer.to_ulong() != rec.total - 1) {
@ -428,6 +442,9 @@ td::Result<std::unique_ptr<ValidatorSet>> Config::unpack_validator_set(Ref<vm::C
ptr->list.emplace_back(sig_pubkey.pubkey, descr.weight, ptr->total_weight, descr.adnl_addr); ptr->list.emplace_back(sig_pubkey.pubkey, descr.weight, ptr->total_weight, descr.adnl_addr);
ptr->total_weight += descr.weight; ptr->total_weight += descr.weight;
} }
if (rec.total_weight && rec.total_weight != ptr->total_weight) {
return td::Status::Error("validator set declares incorrect total weight");
}
return std::move(ptr); return std::move(ptr);
} }
@ -517,6 +534,58 @@ td::Result<std::vector<StoragePrices>> Config::get_storage_prices() const {
return std::move(res); return std::move(res);
} }
td::Result<GasLimitsPrices> Config::get_gas_limits_prices(bool is_masterchain) const {
GasLimitsPrices res;
auto id = is_masterchain ? 20 : 21;
auto cell = get_config_param(id);
if (cell.is_null()) {
return td::Status::Error(PSLICE() << "configuration parameter " << id << " with gas prices is absent");
}
auto cs = vm::load_cell_slice(std::move(cell));
block::gen::GasLimitsPrices::Record_gas_flat_pfx flat;
if (tlb::unpack(cs, flat)) {
cs = *flat.other;
res.flat_gas_limit = flat.flat_gas_limit;
res.flat_gas_price = flat.flat_gas_price;
}
auto f = [&](const auto& r, td::uint64 spec_limit) {
res.gas_limit = r.gas_limit;
res.special_gas_limit = spec_limit;
res.gas_credit = r.gas_credit;
res.gas_price = r.gas_price;
res.freeze_due_limit = r.freeze_due_limit;
res.delete_due_limit = r.delete_due_limit;
};
block::gen::GasLimitsPrices::Record_gas_prices_ext rec;
if (tlb::unpack(cs, rec)) {
f(rec, rec.special_gas_limit);
} else {
block::gen::GasLimitsPrices::Record_gas_prices rec0;
if (tlb::unpack(cs, rec0)) {
f(rec0, rec0.gas_limit);
} else {
return td::Status::Error(PSLICE() << "configuration parameter " << id
<< " with gas prices is invalid - can't parse");
}
}
return res;
}
td::Result<MsgPrices> Config::get_msg_prices(bool is_masterchain) const {
auto id = is_masterchain ? 24 : 25;
auto cell = get_config_param(id);
if (cell.is_null()) {
return td::Status::Error(PSLICE() << "configuration parameter " << id << " with msg prices is absent");
}
auto cs = vm::load_cell_slice(std::move(cell));
block::gen::MsgForwardPrices::Record rec;
if (!tlb::unpack(cs, rec)) {
return td::Status::Error(PSLICE() << "configuration parameter " << id
<< " with msg prices is invalid - can't parse");
}
return MsgPrices(rec.lump_price, rec.bit_price, rec.cell_price, rec.ihr_price_factor, rec.first_frac, rec.next_frac);
}
CatchainValidatorsConfig Config::unpack_catchain_validators_config(Ref<vm::Cell> cell) { CatchainValidatorsConfig Config::unpack_catchain_validators_config(Ref<vm::Cell> cell) {
block::gen::CatchainConfig::Record cfg; block::gen::CatchainConfig::Record cfg;
if (cell.is_null() || !tlb::unpack_cell(std::move(cell), cfg)) { if (cell.is_null() || !tlb::unpack_cell(std::move(cell), cfg)) {

View file

@ -50,7 +50,7 @@ struct ValidatorDescr {
: pubkey(_pubkey), weight(_weight), cum_weight(_cum_weight) { : pubkey(_pubkey), weight(_weight), cum_weight(_cum_weight) {
adnl_addr.set_zero(); adnl_addr.set_zero();
} }
bool operator<(td::uint64 wt_pos) const & { bool operator<(td::uint64 wt_pos) const& {
return cum_weight < wt_pos; return cum_weight < wt_pos;
} }
}; };
@ -327,6 +327,46 @@ struct StoragePrices {
, mc_bit_price(_mc_bprice) , mc_bit_price(_mc_bprice)
, mc_cell_price(_mc_cprice) { , mc_cell_price(_mc_cprice) {
} }
static td::RefInt256 compute_storage_fees(ton::UnixTime now, const std::vector<block::StoragePrices>& pricing,
const vm::CellStorageStat& storage_stat, ton::UnixTime last_paid,
bool is_special, bool is_masterchain);
};
struct GasLimitsPrices {
td::uint64 flat_gas_limit{0};
td::uint64 flat_gas_price{0};
td::uint64 gas_price{0};
td::uint64 special_gas_limit{0};
td::uint64 gas_limit{0};
td::uint64 gas_credit{0};
td::uint64 block_gas_limit{0};
td::uint64 freeze_due_limit{0};
td::uint64 delete_due_limit{0};
td::RefInt256 compute_gas_price(td::uint64 gas_used) const;
};
// msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms
// ihr_fwd_fees = ceil((msg_fwd_fees * ihr_price_factor)/2^16) nanograms
// bits in the root cell of a message are not included in msg.bits (lump_price pays for them)
struct MsgPrices {
td::uint64 lump_price;
td::uint64 bit_price;
td::uint64 cell_price;
td::uint32 ihr_factor;
td::uint32 first_frac;
td::uint32 next_frac;
td::uint64 compute_fwd_fees(td::uint64 cells, td::uint64 bits) const;
std::pair<td::uint64, td::uint64> compute_fwd_ihr_fees(td::uint64 cells, td::uint64 bits,
bool ihr_disabled = false) const;
MsgPrices() = default;
MsgPrices(td::uint64 lump, td::uint64 bitp, td::uint64 cellp, td::uint32 ihrf, td::uint32 firstf, td::uint32 nextf)
: lump_price(lump), bit_price(bitp), cell_price(cellp), ihr_factor(ihrf), first_frac(firstf), next_frac(nextf) {
}
td::RefInt256 get_first_part(td::RefInt256 total) const;
td::uint64 get_first_part(td::uint64 total) const;
td::RefInt256 get_next_part(td::RefInt256 total) const;
}; };
struct CatchainValidatorsConfig { struct CatchainValidatorsConfig {
@ -499,6 +539,8 @@ class Config {
bool is_special_smartcontract(const ton::StdSmcAddress& addr) const; bool is_special_smartcontract(const ton::StdSmcAddress& addr) const;
static td::Result<std::unique_ptr<ValidatorSet>> unpack_validator_set(Ref<vm::Cell> valset_root); static td::Result<std::unique_ptr<ValidatorSet>> unpack_validator_set(Ref<vm::Cell> valset_root);
td::Result<std::vector<StoragePrices>> get_storage_prices() const; td::Result<std::vector<StoragePrices>> get_storage_prices() const;
td::Result<GasLimitsPrices> get_gas_limits_prices(bool is_masterchain = false) const;
td::Result<MsgPrices> get_msg_prices(bool is_masterchain = false) const;
static CatchainValidatorsConfig unpack_catchain_validators_config(Ref<vm::Cell> cell); static CatchainValidatorsConfig unpack_catchain_validators_config(Ref<vm::Cell> cell);
CatchainValidatorsConfig get_catchain_validators_config() const; CatchainValidatorsConfig get_catchain_validators_config() const;
td::Status visit_validator_params() const; td::Status visit_validator_params() const;

View file

@ -421,7 +421,9 @@ void add_partial_storage_payment(td::BigInt256& payment, ton::UnixTime delta, co
payment += b; payment += b;
} }
td::RefInt256 Account::compute_storage_fees(ton::UnixTime now, const std::vector<block::StoragePrices>& pricing) const { td::RefInt256 StoragePrices::compute_storage_fees(ton::UnixTime now, const std::vector<block::StoragePrices>& pricing,
const vm::CellStorageStat& storage_stat, ton::UnixTime last_paid,
bool is_special, bool is_masterchain) {
if (now <= last_paid || !last_paid || is_special || pricing.empty() || now <= pricing[0].valid_since) { if (now <= last_paid || !last_paid || is_special || pricing.empty() || now <= pricing[0].valid_since) {
return {}; return {};
} }
@ -438,7 +440,7 @@ td::RefInt256 Account::compute_storage_fees(ton::UnixTime now, const std::vector
ton::UnixTime valid_until = (i < n - 1 ? std::min(now, pricing[i + 1].valid_since) : now); ton::UnixTime valid_until = (i < n - 1 ? std::min(now, pricing[i + 1].valid_since) : now);
if (upto < valid_until) { if (upto < valid_until) {
assert(upto >= pricing[i].valid_since); assert(upto >= pricing[i].valid_since);
add_partial_storage_payment(total.unique_write(), valid_until - upto, pricing[i], storage_stat, is_masterchain()); add_partial_storage_payment(total.unique_write(), valid_until - upto, pricing[i], storage_stat, is_masterchain);
} }
upto = valid_until; upto = valid_until;
} }
@ -446,6 +448,10 @@ td::RefInt256 Account::compute_storage_fees(ton::UnixTime now, const std::vector
return total; return total;
} }
td::RefInt256 Account::compute_storage_fees(ton::UnixTime now, const std::vector<block::StoragePrices>& pricing) const {
return StoragePrices::compute_storage_fees(now, pricing, storage_stat, last_paid, is_special, is_masterchain());
}
Transaction::Transaction(const Account& _account, int ttype, ton::LogicalTime req_start_lt, ton::UnixTime _now, Transaction::Transaction(const Account& _account, int ttype, ton::LogicalTime req_start_lt, ton::UnixTime _now,
Ref<vm::Cell> _inmsg) Ref<vm::Cell> _inmsg)
: trans_type(ttype) : trans_type(ttype)
@ -969,7 +975,7 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
gas = vm.get_gas_limits(); gas = vm.get_gas_limits();
cp.gas_used = std::min<long long>(gas.gas_consumed(), gas.gas_limit); cp.gas_used = std::min<long long>(gas.gas_consumed(), gas.gas_limit);
cp.accepted = (gas.gas_credit == 0); cp.accepted = (gas.gas_credit == 0);
cp.success = (cp.accepted && (unsigned)cp.exit_code <= 1); cp.success = (cp.accepted && vm.committed());
if (cp.accepted & use_msg_state) { if (cp.accepted & use_msg_state) {
was_activated = true; was_activated = true;
acc_status = Account::acc_active; acc_status = Account::acc_active;
@ -978,8 +984,8 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
<< ", limit=" << gas.gas_limit << ", credit=" << gas.gas_credit; << ", limit=" << gas.gas_limit << ", credit=" << gas.gas_credit;
LOG(INFO) << "out_of_gas=" << cp.out_of_gas << ", accepted=" << cp.accepted << ", success=" << cp.success; LOG(INFO) << "out_of_gas=" << cp.out_of_gas << ", accepted=" << cp.accepted << ", success=" << cp.success;
if (cp.success) { if (cp.success) {
cp.new_data = vm.get_c4(); // c4 -> persistent data cp.new_data = vm.get_committed_state().c4; // c4 -> persistent data
cp.actions = vm.get_d(5); // c5 -> action list cp.actions = vm.get_committed_state().c5; // c5 -> action list
int out_act_num = output_actions_count(cp.actions); int out_act_num = output_actions_count(cp.actions);
if (verbosity > 2) { if (verbosity > 2) {
std::cerr << "new smart contract data: "; std::cerr << "new smart contract data: ";

View file

@ -65,10 +65,10 @@ struct NewOutMsg {
NewOutMsg(ton::LogicalTime _lt, Ref<vm::Cell> _msg, Ref<vm::Cell> _trans) NewOutMsg(ton::LogicalTime _lt, Ref<vm::Cell> _msg, Ref<vm::Cell> _trans)
: lt(_lt), msg(std::move(_msg)), trans(std::move(_trans)) { : lt(_lt), msg(std::move(_msg)), trans(std::move(_trans)) {
} }
bool operator<(const NewOutMsg& other) const & { bool operator<(const NewOutMsg& other) const& {
return lt < other.lt || (lt == other.lt && msg->get_hash() < other.msg->get_hash()); return lt < other.lt || (lt == other.lt && msg->get_hash() < other.msg->get_hash());
} }
bool operator>(const NewOutMsg& other) const & { bool operator>(const NewOutMsg& other) const& {
return lt > other.lt || (lt == other.lt && other.msg->get_hash() < msg->get_hash()); return lt > other.lt || (lt == other.lt && other.msg->get_hash() < msg->get_hash());
} }
}; };
@ -132,29 +132,6 @@ struct ComputePhaseConfig {
bool parse_GasLimitsPrices(Ref<vm::Cell> cell, td::RefInt256& freeze_due_limit, td::RefInt256& delete_due_limit); bool parse_GasLimitsPrices(Ref<vm::Cell> cell, td::RefInt256& freeze_due_limit, td::RefInt256& delete_due_limit);
}; };
// msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms
// ihr_fwd_fees = ceil((msg_fwd_fees * ihr_price_factor)/2^16) nanograms
// bits in the root cell of a message are not included in msg.bits (lump_price pays for them)
struct MsgPrices {
td::uint64 lump_price;
td::uint64 bit_price;
td::uint64 cell_price;
td::uint32 ihr_factor;
td::uint32 first_frac;
td::uint32 next_frac;
td::uint64 compute_fwd_fees(td::uint64 cells, td::uint64 bits) const;
std::pair<td::uint64, td::uint64> compute_fwd_ihr_fees(td::uint64 cells, td::uint64 bits,
bool ihr_disabled = false) const;
MsgPrices() = default;
MsgPrices(td::uint64 lump, td::uint64 bitp, td::uint64 cellp, td::uint32 ihrf, td::uint32 firstf, td::uint32 nextf)
: lump_price(lump), bit_price(bitp), cell_price(cellp), ihr_factor(ihrf), first_frac(firstf), next_frac(nextf) {
}
td::RefInt256 get_first_part(td::RefInt256 total) const;
td::uint64 get_first_part(td::uint64 total) const;
td::RefInt256 get_next_part(td::RefInt256 total) const;
};
struct ActionPhaseConfig { struct ActionPhaseConfig {
int max_actions{255}; int max_actions{255};
MsgPrices fwd_std; MsgPrices fwd_std;

View file

@ -224,6 +224,8 @@ class BitSliceGen {
BitSliceGen(BitSliceGen&& bs, unsigned _offs, unsigned _len); BitSliceGen(BitSliceGen&& bs, unsigned _offs, unsigned _len);
BitSliceGen(Pt* _ptr, unsigned _len) : ref(), ptr(_ptr), offs(0), len(_len) { BitSliceGen(Pt* _ptr, unsigned _len) : ref(), ptr(_ptr), offs(0), len(_len) {
} }
explicit BitSliceGen(Slice slice) : BitSliceGen(slice.data(), slice.size() * 8) {
}
~BitSliceGen() { ~BitSliceGen() {
} }
Pt* get_ptr() const { Pt* get_ptr() const {

View file

@ -970,6 +970,7 @@ x{F4B7} @Defop SUBDICTURPGET
x{F800} @Defop ACCEPT x{F800} @Defop ACCEPT
x{F801} @Defop SETGASLIMIT x{F801} @Defop SETGASLIMIT
x{F80F} @Defop COMMIT
x{F82} @Defop(4u) GETPARAM x{F82} @Defop(4u) GETPARAM
x{F823} @Defop NOW x{F823} @Defop NOW

View file

@ -139,7 +139,7 @@ recursive append-long-bytes {
// ( S -- x ) parse public key // ( S -- x ) parse public key
{ dup $len 48 <> abort"public key must be 48 characters long" { dup $len 48 <> abort"public key must be 48 characters long"
base64>B dup Blen 36 <> abort"public key must be 48 characters long" base64url>B dup Blen 36 <> abort"public key must be 48 characters long"
34 B| 16 B>u@ over crc16 <> abort"crc16 mismatch in public key" 34 B| 16 B>u@ over crc16 <> abort"crc16 mismatch in public key"
16 B>u@+ 0x3ee6 <> abort"invalid tag in public key" 16 B>u@+ 0x3ee6 <> abort"invalid tag in public key"
256 B>u@ 256 B>u@

View file

@ -1,4 +1,5 @@
"Asm.fif" include "Asm.fif" include
"TonUtil.fif" include
31 -1<< constant wc_undef 31 -1<< constant wc_undef
0 constant wc_base 0 constant wc_base
@ -187,6 +188,7 @@ dictnew constant special-dict
// restricted wallet creation // restricted wallet creation
"auto/wallet-code.fif" include =: WCode0
"auto/restricted-wallet-code.fif" include =: RWCode1 "auto/restricted-wallet-code.fif" include =: RWCode1
"auto/restricted-wallet2-code.fif" include =: RWCode2 "auto/restricted-wallet2-code.fif" include =: RWCode2
@ -200,7 +202,7 @@ dictnew constant special-dict
0 // ticktock 0 // ticktock
2 // mode: create 2 // mode: create
register_smc register_smc
Masterchain 6 .Addr cr Masterchain swap 6 .Addr cr
} : create-wallet1 } : create-wallet1
// D x t -- D' // D x t -- D'
@ -225,5 +227,18 @@ dictnew constant special-dict
0 // ticktock 0 // ticktock
2 // mode: create 2 // mode: create
register_smc register_smc
Masterchain 6 .Addr cr Masterchain swap 6 .Addr cr
} : create-wallet2 } : create-wallet2
// pubkey amount
{ over ."Key " pubkey>$ type ." -> "
WCode0 // code
<b 1 32 u, 3 roll 256 u, b> // data
empty_cell // libs
3 roll // balance
0 // split_depth
0 // ticktock
2 // mode: create
register_smc
Masterchain swap 6 .Addr cr
} : create-wallet0

View file

@ -1,15 +1,32 @@
;; Simple configuration smart contract ;; Simple configuration smart contract
() set_conf_param(int index, cell value) impure { () set_conf_param(int index, cell value) impure {
var cs = begin_parse(get_data()); var cs = get_data().begin_parse();
var cfg_dict = cs~load_ref(); var cfg_dict = cs~load_ref();
cfg_dict~idict_set_ref(32, index, value); cfg_dict~idict_set_ref(32, index, value);
set_data(begin_cell().store_ref(cfg_dict).store_slice(cs).end_cell()); set_data(begin_cell().store_ref(cfg_dict).store_slice(cs).end_cell());
} }
(cell, int, int, cell) load_data() inline {
var cs = get_data().begin_parse();
var (cfg_dict, stored_seqno, public_key) = (cs~load_ref(), cs~load_uint(32), cs~load_uint(256));
var vote_dict = cs.slice_empty?() ? new_dict() : cs~load_dict();
cs.end_parse();
return (cfg_dict, stored_seqno, public_key, vote_dict);
}
() store_data(cfg_dict, stored_seqno, public_key, vote_dict) impure inline {
set_data(begin_cell()
.store_ref(cfg_dict)
.store_uint(stored_seqno, 32)
.store_uint(public_key, 256)
.store_dict(vote_dict)
.end_cell());
}
(int, int) check_validator_set(cell vset) { (int, int) check_validator_set(cell vset) {
var cs = vset.begin_parse(); var cs = vset.begin_parse();
throw_unless(9, cs~load_uint(8) == 0x11); ;; validators#11 throw_if(9, (cs~load_uint(8) - 0x11) & -2); ;; validators#11 or validators_ext#12
int utime_since = cs~load_uint(32); int utime_since = cs~load_uint(32);
int utime_until = cs~load_uint(32); int utime_until = cs~load_uint(32);
int total = cs~load_uint(16); int total = cs~load_uint(16);
@ -86,6 +103,83 @@
.end_cell(), 0); .end_cell(), 0);
} }
() after_code_upgrade(slice param, cell old_code) impure method_id(1666) {
}
_ perform_action(cfg_dict, public_key, action, cs) {
if (action == 0x43665021) {
;; change one configuration parameter
var param_index = cs~load_uint(32);
var param_value = cs~load_ref();
cs.end_parse();
cfg_dict~idict_set_ref(32, param_index, param_value);
return (cfg_dict, public_key);
} elseif (action == 0x4e436f64) {
;; change configuration smart contract code
var new_code = cs~load_ref();
set_code(new_code);
var old_code = get_c3();
set_c3(new_code);
after_code_upgrade(cs, old_code);
throw(0);
return (cfg_dict, public_key);
} elseif (action == 0x50624b21) {
;; change configuration master public key
public_key = cs~load_uint(256);
cs.end_parse();
return (cfg_dict, public_key);
} elseif (action == 0x4e43ef05) {
;; change election smart contract code
change_elector_code(cs);
return (cfg_dict, public_key);
} else {
throw_if(32, action);
return (cfg_dict, public_key);
}
}
slice get_validator_descr(int idx) inline_ref {
var vset = config_param(34);
if (vset.null?()) {
return null();
}
var cs = begin_parse(vset);
cs~skip_bits(8 + 32 + 32 + 16 + 16);
var dict = begin_cell().store_slice(cs).end_cell();
var (value, _) = dict.udict_get?(16, idx);
return value;
}
(int, int) unpack_validator_descr(slice cs) inline {
;; ed25519_pubkey#8e81278a pubkey:bits256 = SigPubKey;
;; validator#53 public_key:SigPubKey weight:uint64 = ValidatorDescr;
;; validator_addr#73 public_key:SigPubKey weight:uint64 adnl_addr:bits256 = ValidatorDescr;
throw_unless(41, (cs~load_uint(8) & ~ 0x20) == 0x53);
throw_unless(41, cs~load_uint(32) == 0x8e81278a);
return (cs~load_uint(256), cs~load_uint(64));
}
slice create_new_entry(cs) inline {
return begin_cell().store_int(false, 1).store_uint(0, 64).store_uint(0, 256).store_slice(cs).end_cell().begin_parse();
}
cell register_vote(vote_dict, action, cs, idx, weight) {
int hash = 0;
var entry = null();
if (action & 1) {
hash = slice_hash(cs);
(entry, var found?) = vote_dict.udict_get?(256, hash);
ifnot (found?) {
entry = create_new_entry(cs);
}
} else {
hash = cs.preload_uint(256);
(entry, var found?) = vote_dict.udict_get?(256, hash);
throw_unless(42, found?);
}
return vote_dict;
}
() recv_external(slice in_msg) impure { () recv_external(slice in_msg) impure {
var signature = in_msg~load_bits(512); var signature = in_msg~load_bits(512);
var cs = in_msg; var cs = in_msg;
@ -93,36 +187,28 @@
int msg_seqno = cs~load_uint(32); int msg_seqno = cs~load_uint(32);
var valid_until = cs~load_uint(32); var valid_until = cs~load_uint(32);
throw_if(35, valid_until < now()); throw_if(35, valid_until < now());
var cs2 = begin_parse(get_data()); var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data();
var cfg_dict = cs2~load_ref();
var stored_seqno = cs2~load_uint(32);
var public_key = cs2~load_uint(256);
cs2.end_parse();
throw_unless(33, msg_seqno == stored_seqno); throw_unless(33, msg_seqno == stored_seqno);
ifnot ((action - 0x566f7465) & -2) {
var idx = cs~load_uint(16);
var vdescr = get_validator_descr(idx);
var (val_pubkey, weight) = unpack_validator_descr(vdescr);
throw_unless(34, check_signature(slice_hash(in_msg), signature, val_pubkey));
accept_message();
stored_seqno += 1;
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
commit();
vote_dict = register_vote(vote_dict, action, cs, idx, weight);
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
return ();
}
throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key)); throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key));
accept_message(); accept_message();
if (action == 0x43665021) { stored_seqno += 1;
;; change one configuration parameter store_data(cfg_dict, stored_seqno, public_key, vote_dict);
var param_index = cs~load_uint(32); commit();
var param_value = cs~load_ref(); (cfg_dict, public_key) = perform_action(cfg_dict, public_key, action, cs);
cs.end_parse(); store_data(cfg_dict, stored_seqno, public_key, vote_dict);
cfg_dict~idict_set_ref(32, param_index, param_value);
} elseif (action == 0x4e436f64) {
;; change configuration smart contract code
var new_code = cs~load_ref();
cs.end_parse();
set_code(new_code);
} elseif (action == 0x50624b21) {
;; change configuration master public key
public_key = cs~load_uint(256);
cs.end_parse();
} elseif (action == 0x4e43ef05) {
;; change election smart contract code
change_elector_code(cs);
} else {
throw_if(32, action);
}
set_data(begin_cell().store_ref(cfg_dict).store_uint(stored_seqno + 1, 32).store_uint(public_key, 256).end_cell());
} }
() run_ticktock(int is_tock) impure { () run_ticktock(int is_tock) impure {

View file

@ -408,7 +408,7 @@ _ compute_total_stake(l, n, m_stake) {
return tot_stake; return tot_stake;
} }
(cell, cell, cell, int, int) try_elect(credits, members, min_stake, max_stake, min_total_stake, max_stake_factor) { (cell, cell, int, cell, int, int) try_elect(credits, members, min_stake, max_stake, min_total_stake, max_stake_factor) {
var cs = 16.config_param().begin_parse(); var cs = 16.config_param().begin_parse();
var (max_validators, _, min_validators) = (cs~load_uint(16), cs~load_uint(16), cs~load_uint(16)); var (max_validators, _, min_validators) = (cs~load_uint(16), cs~load_uint(16), cs~load_uint(16));
cs.end_parse(); cs.end_parse();
@ -435,7 +435,7 @@ _ compute_total_stake(l, n, m_stake) {
} until (~ f); } until (~ f);
n = min(n, max_validators); n = min(n, max_validators);
if (n < min_validators) { if (n < min_validators) {
return (credits, new_dict(), new_dict(), 0, 0); return (credits, new_dict(), 0, new_dict(), 0, 0);
} }
var l = nil; var l = nil;
do { do {
@ -464,7 +464,7 @@ _ compute_total_stake(l, n, m_stake) {
} }
} until (i >= n); } until (i >= n);
if ((m == 0) | (best_stake < min_total_stake)) { if ((m == 0) | (best_stake < min_total_stake)) {
return (credits, new_dict(), new_dict(), 0, 0); return (credits, new_dict(), 0, new_dict(), 0, 0);
} }
;; we have to select first m validators from list l ;; we have to select first m validators from list l
l1 = touch(l); l1 = touch(l);
@ -476,6 +476,7 @@ _ compute_total_stake(l, n, m_stake) {
;; create both the new validator set and the refund set ;; create both the new validator set and the refund set
int i = 0; int i = 0;
var tot_stake = 0; var tot_stake = 0;
var tot_weight = 0;
var vset = new_dict(); var vset = new_dict();
var frozen = new_dict(); var frozen = new_dict();
do { do {
@ -492,6 +493,7 @@ _ compute_total_stake(l, n, m_stake) {
;; validator_addr#73 public_key:SigPubKey weight:uint64 adnl_addr:bits256 = ValidatorDescr; ;; validator_addr#73 public_key:SigPubKey weight:uint64 adnl_addr:bits256 = ValidatorDescr;
var weight = (true_stake << 60) / best_stake; var weight = (true_stake << 60) / best_stake;
tot_stake += true_stake; tot_stake += true_stake;
tot_weight += weight;
var vinfo = begin_cell() var vinfo = begin_cell()
.store_uint(adnl_addr ? 0x73 : 0x53, 8) ;; validator_addr#73 or validator#53 .store_uint(adnl_addr ? 0x73 : 0x53, 8) ;; validator_addr#73 or validator#53
.store_uint(0x8e81278a, 32) ;; ed25519_pubkey#8e81278a .store_uint(0x8e81278a, 32) ;; ed25519_pubkey#8e81278a
@ -514,7 +516,7 @@ _ compute_total_stake(l, n, m_stake) {
i += 1; i += 1;
} until (l.null?()); } until (l.null?());
throw_unless(49, tot_stake == best_stake); throw_unless(49, tot_stake == best_stake);
return (credits, vset, frozen, tot_stake, m); return (credits, vset, tot_weight, frozen, tot_stake, m);
} }
int conduct_elections(ds, elect, credits) impure { int conduct_elections(ds, elect, credits) impure {
@ -545,7 +547,7 @@ int conduct_elections(ds, elect, credits) impure {
;; elections finished ;; elections finished
return false; return false;
} }
(credits, var vdict, var frozen, var total_stakes, var cnt) = try_elect(credits, members, min_stake, max_stake, min_total_stake, max_stake_factor); (credits, var vdict, var total_weight, var frozen, var total_stakes, var cnt) = try_elect(credits, members, min_stake, max_stake, min_total_stake, max_stake_factor);
;; pack elections; if cnt==0, set failed=true, finished=false. ;; pack elections; if cnt==0, set failed=true, finished=false.
failed = (cnt == 0); failed = (cnt == 0);
finished = ~ failed; finished = ~ failed;
@ -561,12 +563,13 @@ int conduct_elections(ds, elect, credits) impure {
var start = max(now() + elect_end_before - 60, elect_at); var start = max(now() + elect_end_before - 60, elect_at);
var main_validators = config_param(16).begin_parse().skip_bits(16).preload_uint(16); var main_validators = config_param(16).begin_parse().skip_bits(16).preload_uint(16);
var vset = begin_cell() var vset = begin_cell()
.store_uint(0x11, 8) ;; validators#11 .store_uint(0x12, 8) ;; validators_ext#12
.store_uint(start, 32) ;; utime_since:uint32 .store_uint(start, 32) ;; utime_since:uint32
.store_uint(start + elect_for, 32) ;; utime_until:uint32 .store_uint(start + elect_for, 32) ;; utime_until:uint32
.store_uint(cnt, 16) ;; total:(## 16) .store_uint(cnt, 16) ;; total:(## 16)
.store_uint(min(cnt, main_validators), 16) ;; main:(## 16) .store_uint(min(cnt, main_validators), 16) ;; main:(## 16)
.store_slice(vdict.begin_parse()) ;; list:(Hashmap 16 ValidatorDescr) .store_uint(total_weight, 64) ;; total_weight:uint64
.store_dict(vdict) ;; list:(HashmapE 16 ValidatorDescr)
.end_cell(); .end_cell();
var config_addr = config_param(0).begin_parse().preload_uint(256); var config_addr = config_param(0).begin_parse().preload_uint(256);
send_validator_set_to_config(config_addr, vset, elect_at); send_validator_set_to_config(config_addr, vset, elect_at);

View file

@ -225,6 +225,22 @@ Masterchain swap
"config-master" +suffix +".addr" save-address-verbose "config-master" +suffix +".addr" save-address-verbose
// Other data // Other data
/*
*
* Initial wallets (test)
*
*/
// pubkey amount `create-wallet1` or pubkey amount `create-wallet2`
PK'PuZPPXK5Rff9SvtoS7Y9lUuEixvy-J6aishYFj3Qn6P0pJMb GR$100000000 create-wallet1
PK'PuYiB1zAWzr4p8j6I681+sGUrRGcn6Ylf7vXl0xaUl/w6Xfg GR$1700000000 create-wallet0
/*
*
* Create state
*
*/
create_state create_state
cr cr ."new state is:" cr dup <s csr. cr cr cr ."new state is:" cr dup <s csr. cr
dup 31 boc+>B dup Bx. cr dup 31 boc+>B dup Bx. cr

View file

@ -0,0 +1,265 @@
;; Simple wallet smart contract
_ unpack_state() inline_ref {
var ds = begin_parse(get_data());
var res = (ds~load_uint(8), ds~load_uint(8), ds~load_uint(64), ds~load_dict(), ds~load_dict());
ds.end_parse();
return res;
}
_ pack_state(cell pending_queries, cell public_keys, int last_cleaned, int k, int n) inline_ref {
return begin_cell()
.store_uint(n, 8)
.store_uint(k, 8)
.store_uint(last_cleaned, 64)
.store_dict(public_keys)
.store_dict(pending_queries)
.end_cell();
}
(int, int) check_signatures(cell public_keys, cell signatures, int hash, int cnt_bits) inline_ref {
int cnt = 0;
do {
slice cs = signatures.begin_parse();
slice signature = cs~load_bits(512);
int i = cs~load_uint(8);
signatures = cs~load_dict();
(slice public_key, var found?) = public_keys.udict_get?(8, i);
throw_unless(37, found?);
throw_unless(38, check_signature(hash, signature, public_key.preload_uint(256)));
int mask = (1 << i);
int old_cnt_bits = cnt_bits;
cnt_bits |= mask;
int should_check = cnt_bits != old_cnt_bits;
cnt -= should_check;
} until (cell_null?(signatures));
return (cnt, cnt_bits);
}
() recv_internal(slice in_msg) impure {
;; do nothing for internal messages
}
(int, int, slice) unpack_query_data(slice in_msg, int n, slice query, var found?) inline_ref {
if (found?) {
throw_unless(35, query~load_int(1));
(int cnt, int cnt_bits, slice msg) = (query~load_uint(8), query~load_uint(n), query);
throw_unless(36, slice_hash(msg) == slice_hash(in_msg));
return (cnt, cnt_bits, msg);
}
return (0, 0, in_msg);
}
() try_init() impure inline_ref {
;; first query without signatures is always accepted
(int n, int k, int last_cleaned, cell public_keys, cell pending_queries) = unpack_state();
throw_if(37, last_cleaned);
accept_message();
set_data(pack_state(pending_queries, public_keys, 1, k, n));
}
cell update_pending_queries(cell pending_queries, slice msg, int query_id, int cnt, int cnt_bits, int n, int k) impure inline_ref {
if (cnt >= k) {
while (msg.slice_refs()) {
var mode = msg~load_uint(8);
send_raw_message(msg~load_ref(), mode);
}
pending_queries~udict_set_builder(64, query_id, begin_cell().store_int(0, 1));
} else {
pending_queries~udict_set_builder(64, query_id, begin_cell()
.store_uint(1, 1)
.store_uint(cnt, 8)
.store_uint(cnt_bits, n)
.store_slice(msg));
}
return pending_queries;
}
() recv_external(slice in_msg) impure {
;; empty message triggers init
if (slice_empty?(in_msg)) {
return try_init();
}
;; Check root signature
slice root_signature = in_msg~load_bits(512);
int root_hash = slice_hash(in_msg);
int root_i = in_msg~load_uint(8);
(int n, int k, int last_cleaned, cell public_keys, cell pending_queries) = unpack_state();
last_cleaned -= last_cleaned == 0;
(slice public_key, var found?) = public_keys.udict_get?(8, root_i);
throw_unless(31, found?);
throw_unless(32, check_signature(root_hash, root_signature, public_key.preload_uint(256)));
cell signatures = in_msg~load_dict();
var hash = slice_hash(in_msg);
int query_id = in_msg~load_uint(64);
var bound = (now() << 32);
throw_if(33, query_id < bound);
(slice query, var found?) = pending_queries.udict_get?(64, query_id);
(int cnt, int cnt_bits, slice msg) = unpack_query_data(in_msg, n, query, found?);
int mask = 1 << root_i;
throw_if(34, cnt_bits & mask);
cnt_bits |= mask;
cnt += 1;
;; TODO: reserve some gas or FAIL
accept_message();
pending_queries = update_pending_queries(pending_queries, msg, query_id, cnt, cnt_bits, n, k);
set_data(pack_state(pending_queries, public_keys, last_cleaned, k, n));
commit();
int need_save = 0;
ifnot (cell_null?(signatures) | (cnt >= k)) {
(int new_cnt, cnt_bits) = check_signatures(public_keys, signatures, hash, cnt_bits);
cnt += new_cnt;
pending_queries = update_pending_queries(pending_queries, msg, query_id, cnt, cnt_bits, n, k);
need_save = -1;
}
bound -= (64 << 32); ;; clean up records expired more than 64 seconds ago
int old_last_cleaned = last_cleaned;
do {
var (pending_queries', i, _, f) = pending_queries.udict_delete_get_min(64);
f~touch();
if (f) {
f = (i < bound);
}
if (f) {
pending_queries = pending_queries';
last_cleaned = i;
need_save = -1;
}
} until (~ f);
if (need_save) {
set_data(pack_state(pending_queries, public_keys, last_cleaned, k, n));
}
}
;; Get methods
;; returns -1 for processed queries, 0 for unprocessed, 1 for unknown (forgotten)
(int, int) get_query_state(int query_id) method_id {
(int n, _, int last_cleaned, _, cell pending_queries) = unpack_state();
(slice cs, var found) = pending_queries.udict_get?(64, query_id);
if (found) {
if (cs~load_int(1)) {
cs~load_uint(8);
return (0, cs~load_uint(n));
} else {
return (-1, 0);
}
} else {
return (-(query_id <= last_cleaned), 0);
}
}
int processed?(int query_id) method_id {
(int x, _) = get_query_state(query_id);
return x;
}
cell create_init_state(int n, int k, cell public_keys) method_id {
return pack_state(new_dict(), public_keys, 0, k, n);
}
cell merge_list(cell a, cell b) {
if (cell_null?(a)) {
return b;
}
if (cell_null?(b)) {
return a;
}
slice as = a.begin_parse();
if (as.slice_refs() != 0) {
cell tail = merge_list(as~load_ref(), b);
return begin_cell().store_slice(as).store_ref(tail).end_cell();
}
as~skip_last_bits(1);
;; as~skip_bits(1);
return begin_cell().store_slice(as).store_dict(b).end_cell();
}
cell get_public_keys() method_id {
(_, _, _, cell public_keys, _) = unpack_state();
return public_keys;
}
(int, int) check_query_signatures(cell query) method_id {
slice cs = query.begin_parse();
slice root_signature = cs~load_bits(512);
int root_hash = slice_hash(cs);
int root_i = cs~load_uint(8);
cell public_keys = get_public_keys();
(slice public_key, var found?) = public_keys.udict_get?(8, root_i);
throw_unless(31, found?);
throw_unless(32, check_signature(root_hash, root_signature, public_key.preload_uint(256)));
int mask = 1 << root_i;
cell signatures = cs~load_dict();
if (cell_null?(signatures)) {
return (1, mask);
}
(int cnt, mask) = check_signatures(public_keys, signatures, slice_hash(cs), mask);
return (cnt + 1, mask);
}
cell messages_by_mask(int mask) method_id {
(int n, _, _, _, cell pending_queries) = unpack_state();
int i = -1;
cell a = new_dict();
do {
(i, var cs, var f) = pending_queries.udict_get_next?(64, i);
if (f) {
if (cs~load_int(1)) {
int cnt_bits = cs.skip_bits(8).preload_uint(n);
if (cnt_bits & mask) {
a~udict_set_builder(64, i, begin_cell().store_slice(cs));
}
}
}
} until (~ f);
return a;
}
cell get_messages_unsigned_by_id(int id) method_id {
return messages_by_mask(1 << id);
}
cell get_messages_unsigned() method_id {
return messages_by_mask(~ 0);
}
(int, int) get_n_k() method_id {
(int n, int k, _, _, _) = unpack_state();
return (n, k);
}
cell merge_inner_queries(cell a, cell b) method_id {
slice ca = a.begin_parse();
slice cb = b.begin_parse();
cell list_a = ca~load_dict();
cell list_b = cb~load_dict();
throw_unless(31, slice_hash(ca) == slice_hash(cb));
return begin_cell()
.store_dict(merge_list(list_a, list_b))
.store_slice(ca)
.end_cell();
}

View file

@ -0,0 +1,67 @@
;; Simple wallet smart contract
cell create_state(int seqno, int public_key) {
return begin_cell().store_uint(seqno, 32).store_uint(public_key, 256).end_cell();
}
(int, int) load_state() {
var cs2 = begin_parse(get_data());
return (cs2~load_uint(32), cs2~load_uint(256));
}
() save_state(int seqno, int public_key) impure {
set_data(create_state(seqno, public_key));
}
() recv_internal(slice in_msg) impure {
;; do nothing for internal messages
}
slice do_verify_message(slice in_msg, int seqno, int public_key) {
var signature = in_msg~load_bits(512);
var cs = in_msg;
int msg_seqno = cs~load_uint(32);
throw_unless(33, msg_seqno == seqno);
throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key));
return cs;
}
() recv_external(slice in_msg) impure {
(int stored_seqno, int public_key) = load_state();
var cs = do_verify_message(in_msg, stored_seqno, public_key);
accept_message();
cs~touch_slice();
if (cs.slice_refs()) {
var mode = cs~load_uint(8);
send_raw_message(cs~load_ref(), mode);
}
cs.end_parse();
save_state(stored_seqno + 1, public_key);
}
;; Get methods
int seqno() method_id {
return get_data().begin_parse().preload_uint(32);
}
cell create_init_state(int public_key) method_id {
return create_state(0, public_key);
}
cell prepare_send_message_with_seqno(int mode, cell msg, int seqno) method_id {
return begin_cell().store_uint(seqno, 32).store_uint(mode, 8).store_ref(msg).end_cell();
}
cell prepare_send_message(int mode, cell msg) method_id {
return prepare_send_message_with_seqno(mode, msg, seqno());
}
slice verify_message(slice msg) method_id {
var (stored_seqno, public_key) = load_state();
return do_verify_message(msg, stored_seqno, public_key);
}

View file

@ -16,6 +16,7 @@ forall X -> X first(tuple t) asm "FIRST";
forall X -> X second(tuple t) asm "SECOND"; forall X -> X second(tuple t) asm "SECOND";
forall X -> X third(tuple t) asm "THIRD"; forall X -> X third(tuple t) asm "THIRD";
forall X -> X fourth(tuple t) asm "3 INDEX"; forall X -> X fourth(tuple t) asm "3 INDEX";
forall X -> X null() asm "PUSHNULL";
int now() asm "NOW"; int now() asm "NOW";
slice my_address() asm "MYADDR"; slice my_address() asm "MYADDR";
@ -34,8 +35,10 @@ int check_data_signature(slice data, slice signature, int public_key) asm "CHKSI
cell get_data() asm "c4 PUSH"; cell get_data() asm "c4 PUSH";
() set_data(cell c) impure asm "c4 POP"; () set_data(cell c) impure asm "c4 POP";
cell get_c3() impure asm "c3 PUSH";
() set_c3(cell c) impure asm "c3 POP"; () set_c3(cell c) impure asm "c3 POP";
() accept_message() impure asm "ACCEPT"; () accept_message() impure asm "ACCEPT";
() commit() impure asm "COMMIT";
int min(int x, int y) asm "MIN"; int min(int x, int y) asm "MIN";
int max(int x, int y) asm "MAX"; int max(int x, int y) asm "MAX";
@ -52,7 +55,10 @@ cell preload_ref(slice s) asm "PLDREF";
;; slice preload_bits(slice s, int len) asm "PLDSLICEX"; ;; slice preload_bits(slice s, int len) asm "PLDSLICEX";
(slice, int) load_grams(slice s) asm( -> 1 0) "LDGRAMS"; (slice, int) load_grams(slice s) asm( -> 1 0) "LDGRAMS";
slice skip_bits(slice s, int len) asm "SDSKIPFIRST"; slice skip_bits(slice s, int len) asm "SDSKIPFIRST";
(slice, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST";
slice first_bits(slice s, int len) asm "SDCUTFIRST"; slice first_bits(slice s, int len) asm "SDCUTFIRST";
slice skip_last_bits(slice s, int len) asm "SDSKIPLAST";
(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST";
(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT"; (slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT";
cell preload_dict(slice s) asm "PLDDICT"; cell preload_dict(slice s) asm "PLDDICT";
slice skip_dict(slice s) asm "SKIPDICT"; slice skip_dict(slice s) asm "SKIPDICT";
@ -94,6 +100,16 @@ cell udict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "D
(cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT"; (cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT"; (cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT"; (cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
cell dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, int) udict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADD";
(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACE";
(cell, int) idict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADD";
(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACE";
cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB"; cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB"; (cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB"; cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";

View file

@ -0,0 +1,99 @@
/*
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 "GenericAccount.h"
#include "block/block-auto.h"
#include "block/block-parse.h"
namespace ton {
td::Ref<vm::Cell> GenericAccount::get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) noexcept {
return vm::CellBuilder()
.store_zeroes(2)
.store_ones(2)
.store_zeroes(1)
.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) noexcept {
return block::StdAddress(workchain_id, init_state->get_hash().bits(), true /*bounce*/);
}
void GenericAccount::store_int_message(vm::CellBuilder& cb, const block::StdAddress& dest_address, td::int64 gramms) {
td::BigInt256 dest_addr;
dest_addr.import_bits(dest_address.addr.as_bitslice());
cb.store_zeroes(1)
.store_ones(1)
.store_long(dest_address.bounceable, 1)
.store_zeroes(3)
.store_ones(1)
.store_zeroes(2)
.store_long(dest_address.workchain, 8)
.store_int256(dest_addr, 256);
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms));
cb.store_zeroes(9 + 64 + 32 + 1 + 1);
}
td::Ref<vm::Cell> GenericAccount::create_ext_message(const block::StdAddress& address, td::Ref<vm::Cell> new_state,
td::Ref<vm::Cell> body) noexcept {
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 ton

View file

@ -0,0 +1,31 @@
/*
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 ton {
class GenericAccount {
public:
static td::Ref<vm::Cell> get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) noexcept;
static block::StdAddress get_address(ton::WorkchainId workchain_id, const td::Ref<vm::Cell>& init_state) noexcept;
static td::Ref<vm::Cell> create_ext_message(const block::StdAddress& address, td::Ref<vm::Cell> new_state,
td::Ref<vm::Cell> body) noexcept;
static void store_int_message(vm::CellBuilder& cb, const block::StdAddress& dest_address, td::int64 gramms);
};
} // namespace ton

View file

@ -0,0 +1,171 @@
#include "MultisigWallet.h"
#include "SmartContractCode.h"
#include "vm/dict.h"
#include "td/utils/misc.h"
namespace ton {
MultisigWallet::QueryBuilder::QueryBuilder(td::int64 query_id, td::Ref<vm::Cell> msg, int mode) {
msg_ = vm::CellBuilder().store_long(query_id, 64).store_long(mode, 8).store_ref(std::move(msg)).finalize();
}
void MultisigWallet::QueryBuilder::sign(td::int32 id, td::Ed25519::PrivateKey& pk) {
CHECK(id < td::narrow_cast<td::int32>(mask_.size()));
auto signature = pk.sign(msg_->get_hash().as_slice()).move_as_ok();
mask_.set(id);
vm::CellBuilder cb;
cb.store_bytes(signature.as_slice());
cb.store_long(id, 8);
cb.ensure_throw(cb.store_maybe_ref(std::move(dict_)));
dict_ = cb.finalize();
}
td::Ref<vm::Cell> MultisigWallet::QueryBuilder::create_inner() const {
vm::CellBuilder cb;
cb.ensure_throw(cb.store_maybe_ref(dict_));
return cb.append_cellslice(vm::load_cell_slice(msg_)).finalize();
}
td::Ref<vm::Cell> MultisigWallet::QueryBuilder::create(td::int32 id, td::Ed25519::PrivateKey& pk) const {
auto cell = create_inner();
vm::CellBuilder cb;
cb.store_long(id, 8);
cb.append_cellslice(vm::load_cell_slice(cell));
cell = cb.finalize();
auto signature = pk.sign(cell->get_hash().as_slice()).move_as_ok();
vm::CellBuilder cb2;
cb2.store_bytes(signature.as_slice());
cb2.append_cellslice(vm::load_cell_slice(cell));
return cb2.finalize();
}
td::Ref<MultisigWallet> MultisigWallet::create(td::Ref<vm::Cell> data) {
return td::Ref<MultisigWallet>(true, State{ton::SmartContractCode::multisig(), std::move(data)});
}
int MultisigWallet::processed(td::uint64 query_id) const {
auto res = run_get_method("processed?", {td::make_refint(query_id)});
return res.stack.write().pop_smallint_range(1, -1);
}
MultisigWallet::QueryState MultisigWallet::get_query_state(td::uint64 query_id) const {
auto ans = run_get_method("get_query_state", {td::make_refint(query_id)});
auto mask = ans.stack.write().pop_int();
auto state = ans.stack.write().pop_smallint_range(1, -1);
QueryState res;
if (state == 1) {
res.state = QueryState::Unknown;
} else if (state == 0) {
res.state = QueryState::NotReady;
for (size_t i = 0; i < res.mask.size(); i++) {
if (mask->get_bit(static_cast<int>(i))) {
res.mask.set(i);
}
}
} else {
res.state = QueryState::Sent;
}
return res;
}
std::vector<td::SecureString> MultisigWallet::get_public_keys() const {
auto ans = run_get_method("get_public_keys");
auto dict_root = ans.stack.write().pop_cell();
vm::Dictionary dict(std::move(dict_root), 8);
std::vector<td::SecureString> res;
dict.check_for_each([&](auto cs, auto x, auto y) {
td::SecureString key(32);
cs->prefetch_bytes(key.as_mutable_slice().ubegin(), td::narrow_cast<int>(key.size()));
res.push_back(std::move(key));
return true;
});
return res;
}
td::Ref<vm::Cell> MultisigWallet::create_init_data(std::vector<td::SecureString> public_keys, int k) const {
vm::Dictionary pk(8);
for (size_t i = 0; i < public_keys.size(); i++) {
auto key = pk.integer_key(td::make_refint(i), 8, false);
pk.set_builder(key.bits(), 8, vm::CellBuilder().store_bytes(public_keys[i].as_slice()));
}
auto res = run_get_method("create_init_state",
{td::make_refint(public_keys.size()), td::make_refint(k), pk.get_root_cell()});
CHECK(res.code == 0);
return res.stack.write().pop_cell();
}
td::Ref<vm::Cell> MultisigWallet::create_init_data_fast(std::vector<td::SecureString> public_keys, int k) {
vm::Dictionary pk(8);
for (size_t i = 0; i < public_keys.size(); i++) {
auto key = pk.integer_key(td::make_refint(i), 8, false);
pk.set_builder(key.bits(), 8, vm::CellBuilder().store_bytes(public_keys[i].as_slice()));
}
vm::CellBuilder cb;
cb.store_long(public_keys.size(), 8).store_long(k, 8).store_long(0, 64);
cb.ensure_throw(cb.store_maybe_ref(pk.get_root_cell()));
cb.ensure_throw(cb.store_maybe_ref({}));
return cb.finalize();
}
td::Ref<vm::Cell> MultisigWallet::merge_queries(td::Ref<vm::Cell> a, td::Ref<vm::Cell> b) const {
auto res = run_get_method("merge_queries", {a, b});
return res.stack.write().pop_cell();
}
MultisigWallet::Mask MultisigWallet::to_mask(td::RefInt256 mask) const {
Mask res_mask;
for (size_t i = 0; i < res_mask.size(); i++) {
if (mask->get_bit(static_cast<int>(i))) {
res_mask.set(i);
}
}
return res_mask;
}
std::pair<int, MultisigWallet::Mask> MultisigWallet::check_query_signatures(td::Ref<vm::Cell> a) const {
auto ans = run_get_method("check_query_signatures", {a});
auto mask = ans.stack.write().pop_int();
auto cnt = ans.stack.write().pop_smallint_range(128);
return std::make_pair(cnt, to_mask(mask));
}
std::pair<int, int> MultisigWallet::get_n_k() const {
auto ans = run_get_method("get_n_k");
auto k = ans.stack.write().pop_smallint_range(128);
auto n = ans.stack.write().pop_smallint_range(128);
return std::make_pair(n, k);
}
std::vector<MultisigWallet::Message> MultisigWallet::get_unsigned_messaged(int id) const {
SmartContract::Answer ans;
if (id == -1) {
ans = run_get_method("get_messages_unsigned");
} else {
ans = run_get_method("get_messages_unsigned_by_id", {td::make_refint(id)});
}
auto n_k = get_n_k();
auto cell = ans.stack.write().pop_maybe_cell();
vm::Dictionary dict(std::move(cell), 64);
std::vector<Message> res;
dict.check_for_each([&](auto cs, auto ptr, auto ptr_bits) {
cs.write().skip_first(8);
Message message;
td::BigInt256 query_id;
query_id.import_bits(ptr, ptr_bits, false);
message.query_id = static_cast<td::uint64>(query_id.to_long());
message.signed_by = to_mask(cs.write().fetch_int256(n_k.first, false));
message.message = cs.write().fetch_ref();
res.push_back(std::move(message));
return true;
});
return res;
}
} // namespace ton

View file

@ -0,0 +1,64 @@
#pragma once
#include "vm/cells.h"
#include "SmartContract.h"
#include "Ed25519.h"
#include <bitset>
namespace ton {
class MultisigWallet : public ton::SmartContract {
public:
MultisigWallet(State state) : SmartContract(std::move(state)) {
}
using Mask = std::bitset<128>;
struct QueryState {
enum State { Unknown, NotReady, Sent } state = Unknown;
Mask mask;
};
class QueryBuilder {
public:
QueryBuilder(td::int64 query_id, td::Ref<vm::Cell> msg, int mode = 3);
void sign(td::int32 id, td::Ed25519::PrivateKey& pk);
td::Ref<vm::Cell> create_inner() const;
td::Ref<vm::Cell> create(td::int32 id, td::Ed25519::PrivateKey& pk) const;
Mask get_mask() const {
return mask_;
}
private:
vm::Ref<vm::Cell> dict_;
td::Ref<vm::Cell> msg_;
Mask mask_;
};
MultisigWallet* make_copy() const override {
return new MultisigWallet{state_};
}
// creation
static td::Ref<MultisigWallet> create(td::Ref<vm::Cell> data = {});
td::Ref<vm::Cell> create_init_data(std::vector<td::SecureString> public_keys, int k) const;
static td::Ref<vm::Cell> create_init_data_fast(std::vector<td::SecureString> public_keys, int k);
// get methods
int processed(td::uint64 query_id) const;
QueryState get_query_state(td::uint64 query_id) const;
std::vector<td::SecureString> get_public_keys() const;
td::Ref<vm::Cell> merge_queries(td::Ref<vm::Cell> a, td::Ref<vm::Cell> b) const;
std::pair<int, Mask> check_query_signatures(td::Ref<vm::Cell> a) const;
std::pair<int, int> get_n_k() const;
Mask to_mask(td::RefInt256 mask) const;
struct Message {
td::uint64 query_id;
Mask signed_by;
td::Ref<vm::Cell> message;
};
std::vector<Message> get_unsigned_messaged(int id = -1) const;
};
} // namespace ton

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 "SmartContract.h"
#include "GenericAccount.h"
#include "block/block.h"
#include "block/block-auto.h"
#include "vm/cellslice.h"
#include "vm/cp0.h"
#include "vm/continuation.h"
#include "td/utils/crypto.h"
namespace ton {
namespace {
td::int32 get_method_id(td::Slice method_name) {
unsigned crc = td::crc16(method_name);
return (crc & 0xffff) | 0x10000;
}
td::Ref<vm::Stack> prepare_vm_stack(td::Ref<vm::CellSlice> body) {
td::Ref<vm::Stack> stack_ref{true};
td::RefInt256 acc_addr{true};
//CHECK(acc_addr.write().import_bits(account.addr.cbits(), 256));
vm::Stack& stack = stack_ref.write();
stack.push_int(td::RefInt256{true, 10000000000});
stack.push_int(td::RefInt256{true, 10000000000});
stack.push_cell(vm::CellBuilder().finalize());
stack.push_cellslice(std::move(body));
return stack_ref;
}
td::Ref<vm::Tuple> prepare_vm_c7() {
// TODO: fix initialization of c7
td::BitArray<256> rand_seed;
rand_seed.as_slice().fill(0);
td::RefInt256 rand_seed_int{true};
rand_seed_int.unique_write().import_bits(rand_seed.cbits(), 256, false);
auto tuple = vm::make_tuple_ref(
td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea
td::make_refint(0), // actions:Integer
td::make_refint(0), // msgs_sent:Integer
td::make_refint(0), // unixtime:Integer
td::make_refint(0), // block_lt:Integer
td::make_refint(0), // trans_lt:Integer
std::move(rand_seed_int), // rand_seed:Integer
block::CurrencyCollection(1000000000).as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)]
vm::load_cell_slice_ref(vm::CellBuilder().finalize()) // myself:MsgAddressInt
//vm::StackEntry::maybe(td::Ref<vm::Cell>())
); // global_config:(Maybe Cell) ] = SmartContractInfo;
//LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string();
return vm::make_tuple_ref(std::move(tuple));
}
SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stack> stack, td::Ref<vm::Tuple> c7,
vm::GasLimits gas, bool ignore_chksig) {
auto gas_credit = gas.gas_credit;
vm::init_op_cp0();
vm::DictionaryBase::get_empty_dictionary();
class Logger : public td::LogInterface {
public:
void append(td::CSlice slice) override {
res.append(slice.data(), slice.size());
}
std::string res;
};
Logger logger;
vm::VmLog log{&logger, td::LogOptions::plain()};
if (GET_VERBOSITY_LEVEL() >= VERBOSITY_NAME(DEBUG)) {
log.log_options.level = 4;
log.log_options.fix_newlines = true;
log.log_mask |= vm::VmLog::DumpStack;
} else {
log.log_options.level = 0;
log.log_mask = 0;
}
SmartContract::Answer res;
vm::VmState vm{state.code, std::move(stack), gas, 1, state.data, log};
vm.set_c7(std::move(c7));
vm.set_chksig_always_succeed(ignore_chksig);
try {
res.code = ~vm.run();
} catch (...) {
LOG(FATAL) << "catch unhandled exception";
}
res.new_state = std::move(state);
res.stack = vm.get_stack_ref();
gas = vm.get_gas_limits();
res.gas_used = gas.gas_consumed();
res.accepted = gas.gas_credit == 0;
res.success = (res.accepted && (unsigned)res.code <= 1);
if (GET_VERBOSITY_LEVEL() >= VERBOSITY_NAME(DEBUG)) {
LOG(DEBUG) << "VM log\n" << logger.res;
std::ostringstream os;
res.stack->dump(os);
LOG(DEBUG) << "VM stack:\n" << os.str();
LOG(DEBUG) << "VM exit code: " << res.code;
LOG(DEBUG) << "VM accepted: " << res.accepted;
LOG(DEBUG) << "VM success: " << res.success;
}
if (res.success) {
res.new_state.data = vm.get_c4();
res.actions = vm.get_d(5);
}
LOG_IF(ERROR, gas_credit != 0 && (res.accepted && !res.success))
<< "Accepted but failed with code " << res.code << "\n"
<< res.gas_used << "\n";
return res;
}
} // namespace
td::Ref<vm::CellSlice> SmartContract::empty_slice() {
return vm::load_cell_slice_ref(vm::CellBuilder().finalize());
}
size_t SmartContract::code_size() const {
return vm::std_boc_serialize(state_.code).ok().size();
}
size_t SmartContract::data_size() const {
return vm::std_boc_serialize(state_.data).ok().size();
}
block::StdAddress SmartContract::get_address(WorkchainId workchain_id) const {
return GenericAccount::get_address(workchain_id, get_init_state());
}
td::Ref<vm::Cell> SmartContract::get_init_state() const {
return GenericAccount::get_init_state(get_state().code, get_state().data);
}
SmartContract::Answer SmartContract::run_method(Args args) {
if (!args.c7) {
args.c7 = prepare_vm_c7();
}
if (!args.limits) {
args.limits = vm::GasLimits{(long long)0, (long long)1000000, (long long)10000};
}
CHECK(args.stack);
CHECK(args.method_id);
args.stack.value().write().push_smallint(args.method_id.unwrap());
auto res =
run_smartcont(get_state(), args.stack.unwrap(), args.c7.unwrap(), args.limits.unwrap(), args.ignore_chksig);
state_ = res.new_state;
return res;
}
SmartContract::Answer SmartContract::run_get_method(Args args) const {
if (!args.c7) {
args.c7 = prepare_vm_c7();
}
if (!args.limits) {
args.limits = vm::GasLimits{1000000};
}
if (!args.stack) {
args.stack = td::Ref<vm::Stack>(true);
}
CHECK(args.method_id);
args.stack.value().write().push_smallint(args.method_id.unwrap());
return run_smartcont(get_state(), args.stack.unwrap(), args.c7.unwrap(), args.limits.unwrap(), args.ignore_chksig);
}
SmartContract::Answer SmartContract::run_get_method(td::Slice method, Args args) const {
return run_get_method(args.set_method_id(method));
}
SmartContract::Answer SmartContract::send_external_message(td::Ref<vm::Cell> cell, Args args) {
return run_method(args.set_stack(prepare_vm_stack(vm::load_cell_slice_ref(cell))).set_method_id(-1));
}
} // namespace ton

View file

@ -0,0 +1,116 @@
/*
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 "vm/stack.hpp"
#include "vm/continuation.h"
#include "td/utils/optional.h"
#include "td/utils/crypto.h"
#include "block/block.h"
namespace ton {
class SmartContract : public td::CntObject {
static td::Ref<vm::CellSlice> empty_slice();
public:
struct State {
td::Ref<vm::Cell> code;
td::Ref<vm::Cell> data;
};
SmartContract(State state) : state_(std::move(state)) {
}
struct Answer {
SmartContract::State new_state;
bool accepted;
bool success;
td::Ref<vm::Stack> stack;
td::Ref<vm::Cell> actions;
td::int32 code;
td::int64 gas_used;
};
struct Args {
td::optional<td::int32> method_id;
td::optional<vm::GasLimits> limits;
td::optional<td::Ref<vm::Tuple>> c7;
td::optional<td::Ref<vm::Stack>> stack;
bool ignore_chksig{false};
Args() {
}
Args(std::initializer_list<vm::StackEntry> stack)
: stack(td::Ref<vm::Stack>(true, std::vector<vm::StackEntry>(std::move(stack)))) {
}
Args&& set_method_id(td::Slice method_name) {
unsigned crc = td::crc16(method_name);
return set_method_id((crc & 0xffff) | 0x10000);
}
Args&& set_method_id(td::int32 method_id) {
this->method_id = method_id;
return std::move(*this);
}
Args&& set_limits(vm::GasLimits limits) {
this->limits = std::move(limits);
return std::move(*this);
}
Args&& set_c7(td::Ref<vm::Tuple> c7) {
this->c7 = std::move(c7);
return std::move(*this);
}
Args&& set_stack(std::vector<vm::StackEntry> stack) {
this->stack = td::Ref<vm::Stack>(true, std::move(stack));
return std::move(*this);
}
Args&& set_stack(td::Ref<vm::Stack> stack) {
this->stack = std::move(stack);
return std::move(*this);
}
Args&& set_ignore_chksig(bool ignore_chksig) {
this->ignore_chksig = ignore_chksig;
return std::move(*this);
}
};
Answer run_method(Args args = {});
Answer run_get_method(Args args = {}) const;
Answer run_get_method(td::Slice method, Args args = {}) const;
Answer send_external_message(td::Ref<vm::Cell> cell, Args args = {});
size_t code_size() const;
size_t data_size() const;
static td::Ref<SmartContract> create(State state) {
return td::Ref<SmartContract>{true, std::move(state)};
}
block::StdAddress get_address(WorkchainId workchain_id = basechainId) const;
td::Ref<vm::Cell> get_init_state() const;
const State& get_state() const {
return state_;
}
protected:
State state_;
};
} // namespace ton

View file

@ -0,0 +1,72 @@
/*
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 "SmartContractCode.h"
#include "vm/boc.h"
#include <map>
#include "td/utils/base64.h"
namespace ton {
namespace {
const auto& get_map() {
static auto map = [] {
class Cmp : public std::less<> {
public:
using is_transparent = void;
};
std::map<std::string, td::Ref<vm::Cell>, Cmp> map;
auto with_tvm_code = [&](auto name, td::Slice code_str) {
map[name] = vm::std_boc_deserialize(td::base64_decode(code_str).move_as_ok()).move_as_ok();
};
#include "smartcont/auto/multisig-code.cpp"
#include "smartcont/auto/simple-wallet-ext-code.cpp"
#include "smartcont/auto/simple-wallet-code.cpp"
#include "smartcont/auto/wallet-code.cpp"
return map;
}();
return map;
}
} // namespace
td::Result<td::Ref<vm::Cell>> SmartContractCode::load(td::Slice name) {
auto& map = get_map();
auto it = map.find(name);
if (it == map.end()) {
return td::Status::Error(PSLICE() << "Can't load td::ref<vm::cell " << name);
}
return it->second;
}
td::Ref<vm::Cell> SmartContractCode::multisig() {
auto res = load("multisig").move_as_ok();
return res;
}
td::Ref<vm::Cell> SmartContractCode::wallet() {
auto res = load("wallet").move_as_ok();
return res;
}
td::Ref<vm::Cell> SmartContractCode::simple_wallet() {
auto res = load("simple-wallet").move_as_ok();
return res;
}
td::Ref<vm::Cell> SmartContractCode::simple_wallet_ext() {
static auto res = load("simple-wallet-ext").move_as_ok();
return res;
}
} // namespace ton

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
*/
#include "vm/cells.h"
namespace ton {
class SmartContractCode {
public:
static td::Result<td::Ref<vm::Cell>> load(td::Slice name);
static td::Ref<vm::Cell> multisig();
static td::Ref<vm::Cell> wallet();
static td::Ref<vm::Cell> simple_wallet();
static td::Ref<vm::Cell> simple_wallet_ext();
};
} // namespace ton

View file

@ -0,0 +1,62 @@
/*
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 "TestGiver.h"
#include "GenericAccount.h"
#include "td/utils/base64.h"
namespace ton {
const block::StdAddress& TestGiver::address() noexcept {
static block::StdAddress res =
block::StdAddress::parse("kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny").move_as_ok();
//static block::StdAddress res =
//block::StdAddress::parse("kf9tswzQaryeJ4aAYLy_phLhx4afF1aEvpUVak-2BuA0CmZi").move_as_ok();
return res;
}
vm::CellHash TestGiver::get_init_code_hash() noexcept {
return vm::CellHash::from_slice(td::base64_decode("wDkZp0yR4xo+9+BnuAPfGVjBzK6FPzqdv2DwRq3z3KE=").move_as_ok());
//return vm::CellHash::from_slice(td::base64_decode("YV/IANhoI22HVeatFh6S5LbCHp+5OilARfzW+VQPZgQ=").move_as_ok());
}
td::Ref<vm::Cell> TestGiver::make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Slice message,
const block::StdAddress& dest_address) noexcept {
vm::CellBuilder cb;
GenericAccount::store_int_message(cb, dest_address, gramms);
cb.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();
}
td::Result<td::uint32> TestGiver::get_seqno() const {
return TRY_VM(get_seqno_or_throw());
}
td::Result<td::uint32> TestGiver::get_seqno_or_throw() const {
if (state_.data.is_null()) {
return 0;
}
auto seqno = vm::load_cell_slice(state_.data).fetch_ulong(32);
if (seqno == vm::CellSlice::fetch_ulong_eof) {
return td::Status::Error("Failed to parse seq_no");
}
return static_cast<td::uint32>(seqno);
}
} // namespace ton

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 "SmartContract.h"
#include "block/block.h"
#include "vm/cells/CellString.h"
namespace ton {
class TestGiver : public SmartContract {
public:
explicit TestGiver(State state) : ton::SmartContract(std::move(state)) {
}
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
static const block::StdAddress& address() noexcept;
static vm::CellHash get_init_code_hash() noexcept;
static td::Ref<vm::Cell> make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Slice message,
const block::StdAddress& dest_address) noexcept;
td::Result<td::uint32> get_seqno() const;
private:
td::Result<td::uint32> get_seqno_or_throw() const;
};
} // namespace ton

View file

@ -0,0 +1,91 @@
/*
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 "TestWallet.h"
#include "GenericAccount.h"
#include "vm/boc.h"
#include "td/utils/base64.h"
namespace ton {
td::Ref<vm::Cell> TestWallet::get_init_state(const td::Ed25519::PublicKey& public_key) noexcept {
auto code = get_init_code();
auto data = get_init_data(public_key);
return GenericAccount::get_init_state(std::move(code), std::move(data));
}
td::Ref<vm::Cell> TestWallet::get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept {
std::string seq_no(4, 0);
auto signature =
private_key.sign(vm::CellBuilder().store_bytes(seq_no).finalize()->get_hash().as_slice()).move_as_ok();
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, td::Slice message,
const block::StdAddress& dest_address) noexcept {
td::int32 send_mode = 3;
if (gramms == -1) {
gramms = 0;
send_mode += 128;
}
vm::CellBuilder cb;
GenericAccount::store_int_message(cb, dest_address, gramms);
cb.store_bytes("\0\0\0\0", 4);
vm::CellString::store(cb, message, 35 * 8).ensure();
auto message_inner = cb.finalize();
auto message_outer =
vm::CellBuilder().store_long(seqno, 32).store_long(send_mode, 8).store_ref(message_inner).finalize();
auto signature = private_key.sign(message_outer->get_hash().as_slice()).move_as_ok();
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();
}
td::Ref<vm::Cell> TestWallet::get_init_code() noexcept {
static auto res = [] {
auto serialized_code = td::base64_decode(
"te6ccgEEAQEAAAAAUwAAov8AIN0gggFMl7qXMO1E0NcLH+Ck8mCBAgDXGCDXCx/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() noexcept {
return get_init_code()->get_hash();
}
td::Ref<vm::Cell> TestWallet::get_init_data(const td::Ed25519::PublicKey& public_key) noexcept {
return vm::CellBuilder().store_long(0, 32).store_bytes(public_key.as_octet_string()).finalize();
}
td::Result<td::uint32> TestWallet::get_seqno() const {
return TRY_VM(get_seqno_or_throw());
}
td::Result<td::uint32> TestWallet::get_seqno_or_throw() const {
if (state_.data.is_null()) {
return 0;
}
auto seqno = vm::load_cell_slice(state_.data).fetch_ulong(32);
if (seqno == vm::CellSlice::fetch_ulong_eof) {
return td::Status::Error("Failed to parse seq_no");
}
return static_cast<td::uint32>(seqno);
}
} // namespace ton

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 "smc-envelope/SmartContract.h"
#include "vm/cells.h"
#include "Ed25519.h"
#include "block/block.h"
#include "vm/cells/CellString.h"
namespace ton {
class TestWallet : public ton::SmartContract {
public:
explicit TestWallet(State state) : ton::SmartContract(std::move(state)) {
}
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
static td::Ref<vm::Cell> get_init_state(const td::Ed25519::PublicKey& public_key) noexcept;
static td::Ref<vm::Cell> get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept;
static td::Ref<vm::Cell> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno,
td::int64 gramms, td::Slice message,
const block::StdAddress& dest_address) noexcept;
static td::Ref<vm::Cell> get_init_code() noexcept;
static vm::CellHash get_init_code_hash() noexcept;
static td::Ref<vm::Cell> get_init_data(const td::Ed25519::PublicKey& public_key) noexcept;
td::Result<td::uint32> get_seqno() const;
private:
td::Result<td::uint32> get_seqno_or_throw() const;
};
} // namespace ton

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 "Wallet.h"
#include "GenericAccount.h"
#include "vm/boc.h"
#include "vm/cells/CellString.h"
#include "td/utils/base64.h"
#include <limits>
namespace ton {
td::Ref<vm::Cell> Wallet::get_init_state(const td::Ed25519::PublicKey& public_key) noexcept {
auto code = get_init_code();
auto data = get_init_data(public_key);
return GenericAccount::get_init_state(std::move(code), std::move(data));
}
td::Ref<vm::Cell> Wallet::get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept {
td::uint32 seqno = 0;
td::uint32 valid_until = std::numeric_limits<td::uint32>::max();
auto signature =
private_key
.sign(vm::CellBuilder().store_long(seqno, 32).store_long(valid_until, 32).finalize()->get_hash().as_slice())
.move_as_ok();
return vm::CellBuilder().store_bytes(signature).store_long(seqno, 32).store_long(valid_until, 32).finalize();
}
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) noexcept {
td::int32 send_mode = 3;
if (gramms == -1) {
gramms = 0;
send_mode += 128;
}
vm::CellBuilder cb;
GenericAccount::store_int_message(cb, dest_address, gramms);
cb.store_bytes("\0\0\0\0", 4);
vm::CellString::store(cb, message, 35 * 8).ensure();
auto message_inner = cb.finalize();
auto message_outer = vm::CellBuilder()
.store_long(seqno, 32)
.store_long(valid_until, 32)
.store_long(send_mode, 8)
.store_ref(message_inner)
.finalize();
auto signature = private_key.sign(message_outer->get_hash().as_slice()).move_as_ok();
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();
}
td::Ref<vm::Cell> Wallet::get_init_code() noexcept {
static auto res = [] {
auto serialized_code = td::base64_decode(
"te6ccgEEAQEAAAAAVwAAqv8AIN0gggFMl7qXMO1E0NcLH+Ck8mCDCNcYINMf0x8B+CO78mPtRNDTH9P/"
"0VExuvKhA/kBVBBC+RDyovgAApMg10qW0wfUAvsA6NGkyMsfy//J7VQ=")
.move_as_ok();
return vm::std_boc_deserialize(serialized_code).move_as_ok();
}();
return res;
}
vm::CellHash Wallet::get_init_code_hash() noexcept {
return get_init_code()->get_hash();
}
td::Ref<vm::Cell> Wallet::get_init_data(const td::Ed25519::PublicKey& public_key) noexcept {
return vm::CellBuilder().store_long(0, 32).store_bytes(public_key.as_octet_string()).finalize();
}
td::Result<td::uint32> Wallet::get_seqno() const {
return TRY_VM(get_seqno_or_throw());
}
td::Result<td::uint32> Wallet::get_seqno_or_throw() const {
if (state_.data.is_null()) {
return 0;
}
//FIXME use get method
return static_cast<td::uint32>(vm::load_cell_slice(state_.data).fetch_ulong(32));
}
} // namespace ton

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 "smc-envelope/SmartContract.h"
#include "vm/cells.h"
#include "Ed25519.h"
#include "block/block.h"
#include "vm/cells/CellString.h"
namespace ton {
class Wallet : ton::SmartContract {
public:
explicit Wallet(State state) : ton::SmartContract(std::move(state)) {
}
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
static td::Ref<vm::Cell> get_init_state(const td::Ed25519::PublicKey& public_key) noexcept;
static td::Ref<vm::Cell> get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept;
static td::Ref<vm::Cell> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno,
td::uint32 valid_until, td::int64 gramms, td::Slice message,
const block::StdAddress& dest_address) noexcept;
static td::Ref<vm::Cell> get_init_code() noexcept;
static vm::CellHash get_init_code_hash() noexcept;
static td::Ref<vm::Cell> get_init_data(const td::Ed25519::PublicKey& public_key) noexcept;
td::Result<td::uint32> get_seqno() const;
private:
td::Result<td::uint32> get_seqno_or_throw() const;
};
} // namespace ton

View file

@ -21,8 +21,12 @@
#include "td/utils/misc.h" #include "td/utils/misc.h"
#include "td/utils/Slice.h" #include "td/utils/Slice.h"
#include "td/utils/tests.h" #include "td/utils/tests.h"
#include "td/utils/JsonBuilder.h"
#include "wycheproof.h"
#include <string> #include <string>
#include <utility>
unsigned char fixed_privkey[32] = "abacabadabacabaeabacabadabacaba"; unsigned char fixed_privkey[32] = "abacabadabacabaeabacabadabacaba";
unsigned char fixed_pubkey[32] = {0x6f, 0x9e, 0x5b, 0xde, 0xce, 0x87, 0x21, 0xeb, 0x57, 0x37, 0xfb, unsigned char fixed_pubkey[32] = {0x6f, 0x9e, 0x5b, 0xde, 0xce, 0x87, 0x21, 0xeb, 0x57, 0x37, 0xfb,
@ -142,3 +146,74 @@ TEST(Crypto, ed25519) {
assert(!std::memcmp(secret12, secret21, 32)); assert(!std::memcmp(secret12, secret21, 32));
*/ */
} }
TEST(Crypto, wycheproof) {
std::vector<std::pair<std::string, std::string>> bad_tests;
auto json_str = wycheproof_ed25519();
auto value = td::json_decode(json_str).move_as_ok();
auto &root = value.get_object();
auto test_groups_o = get_json_object_field(root, "testGroups", td::JsonValue::Type::Array, false).move_as_ok();
auto &test_groups = test_groups_o.get_array();
auto from_hexc = [](char c) {
if (c >= '0' && c <= '9') {
return c - '0';
}
return c - 'a' + 10;
};
auto from_hex = [&](td::Slice s) {
CHECK(s.size() % 2 == 0);
std::string res(s.size() / 2, 0);
for (size_t i = 0; i < s.size(); i += 2) {
res[i / 2] = char(from_hexc(s[i]) * 16 + from_hexc(s[i + 1]));
}
return res;
};
for (auto &test_o : test_groups) {
auto &test = test_o.get_object();
auto key_o = get_json_object_field(test, "key", td::JsonValue::Type::Object, false).move_as_ok();
auto sk_str = td::get_json_object_string_field(key_o.get_object(), "sk", false).move_as_ok();
auto pk_str = td::get_json_object_string_field(key_o.get_object(), "pk", false).move_as_ok();
auto pk = td::Ed25519::PublicKey(td::SecureString(from_hex(pk_str)));
auto sk = td::Ed25519::PrivateKey(td::SecureString(from_hex(sk_str)));
CHECK(sk.get_public_key().move_as_ok().as_octet_string().as_slice() == pk.as_octet_string().as_slice());
//auto key =
//td::Ed25519::PrivateKey::from_pem(
//td::SecureString(td::get_json_object_string_field(test, "keyPem", false).move_as_ok()), td::SecureString())
//.move_as_ok();
auto tests_o = get_json_object_field(test, "tests", td::JsonValue::Type::Array, false).move_as_ok();
auto &tests = tests_o.get_array();
for (auto &test_o : tests) {
auto &test = test_o.get_object();
auto id = td::get_json_object_string_field(test, "tcId", false).move_as_ok();
auto comment = td::get_json_object_string_field(test, "comment", false).move_as_ok();
auto sig = from_hex(td::get_json_object_string_field(test, "sig", false).move_as_ok());
auto msg = from_hex(td::get_json_object_string_field(test, "msg", false).move_as_ok());
auto result = td::get_json_object_string_field(test, "result", false).move_as_ok();
auto has_result = pk.verify_signature(msg, sig).is_ok() ? "valid" : "invalid";
if (result != has_result) {
bad_tests.push_back({id, comment});
}
}
}
if (bad_tests.empty()) {
return;
}
LOG(ERROR) << "FAILED: " << td::format::as_array(bad_tests);
}
TEST(Crypto, almost_zero) {
td::SecureString pub(32);
td::SecureString sig(64);
td::SecureString msg(1);
pub.as_mutable_slice().ubegin()[31] = static_cast<unsigned char>(128);
for (td::int32 j = 0; j < 256; j++) {
msg.as_mutable_slice()[0] = (char)j;
if (td::Ed25519::PublicKey(pub.copy()).verify_signature(msg, sig).is_ok()) {
LOG(ERROR) << "FAILED: " << j;
break;
}
}
}

View file

@ -51,34 +51,6 @@
#include <openssl/sha.h> #include <openssl/sha.h>
struct Step {
std::function<void()> func;
td::uint32 weight;
};
class RandomSteps {
public:
RandomSteps(std::vector<Step> steps) : steps_(std::move(steps)) {
for (auto &step : steps_) {
steps_sum_ += step.weight;
}
}
template <class Random>
void step(Random &rnd) {
auto w = rnd() % steps_sum_;
for (auto &step : steps_) {
if (w < step.weight) {
step.func();
break;
}
w -= step.weight;
}
}
private:
std::vector<Step> steps_;
td::int32 steps_sum_ = 0;
};
namespace vm { namespace vm {
std::vector<int> do_get_serialization_modes() { std::vector<int> do_get_serialization_modes() {
@ -879,7 +851,7 @@ TEST(TonDb, DynamicBoc2) {
VLOG(boc) << " OK"; VLOG(boc) << " OK";
}; };
RandomSteps steps({{new_root, 10}, {delete_root, 9}, {commit, 2}, {reset, 1}}); td::RandomSteps steps({{new_root, 10}, {delete_root, 9}, {commit, 2}, {reset, 1}});
while (first_root_id != total_roots) { while (first_root_id != total_roots) {
VLOG(boc) << first_root_id << " " << last_root_id << " " << kv->count("").ok(); VLOG(boc) << first_root_id << " " << last_root_id << " " << kv->count("").ok();
steps.step(rnd); steps.step(rnd);
@ -1732,7 +1704,7 @@ TEST(TonDb, CompactArray) {
fast_array = vm::FastCompactArray(size); fast_array = vm::FastCompactArray(size);
}; };
RandomSteps steps({{reset_array, 1}, {set_value, 1000}, {validate, 10}, {validate_full, 2}, {flush_to_db, 1}}); td::RandomSteps steps({{reset_array, 1}, {set_value, 1000}, {validate, 10}, {validate_full, 2}, {flush_to_db, 1}});
for (size_t t = 0; t < 100000; t++) { for (size_t t = 0; t < 100000; t++) {
if (t % 10000 == 0) { if (t % 10000 == 0) {
LOG(ERROR) << t; LOG(ERROR) << t;

View file

@ -0,0 +1,524 @@
/*
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 "vm/dict.h"
#include "common/bigint.hpp"
#include "Ed25519.h"
#include "block/block.h"
#include "fift/Fift.h"
#include "fift/words.h"
#include "fift/utils.h"
#include "smc-envelope/GenericAccount.h"
#include "smc-envelope/MultisigWallet.h"
#include "smc-envelope/SmartContract.h"
#include "smc-envelope/SmartContractCode.h"
#include "smc-envelope/TestGiver.h"
#include "smc-envelope/TestWallet.h"
#include "smc-envelope/Wallet.h"
#include "td/utils/base64.h"
#include "td/utils/crypto.h"
#include "td/utils/Random.h"
#include "td/utils/tests.h"
#include "td/utils/ScopeGuard.h"
#include "td/utils/StringBuilder.h"
#include "td/utils/Timer.h"
#include "td/utils/PathView.h"
#include "td/utils/filesystem.h"
#include "td/utils/port/path.h"
#include <bitset>
#include <set>
#include <tuple>
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 // return if recv_internal
DUP 85143 INT EQUAL IFJMP:<{ // "seqno" get-method
DROP c4 PUSHCTR CTOS 32 PLDU // cnt
}>
INC 32 THROWIF // 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:<{
// 3 INT 35 LSHIFT# 3 INT RAWRESERVE // reserve all but 103 Grams from the balance
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();
}
td::Ref<vm::Cell> get_wallet_source() {
std::string code = R"ABCD(
SETCP0 DUP IFNOTRET // return if recv_internal
DUP 85143 INT EQUAL IFJMP:<{ // "seqno" get-method
DROP c4 PUSHCTR CTOS 32 PLDU // cnt
}>
INC 32 THROWIF // fail unless recv_external
9 PUSHPOW2 LDSLICEX DUP 32 LDU 32 LDU // signature in_msg msg_seqno valid_until cs
SWAP NOW LEQ 35 THROWIF // signature in_msg msg_seqno cs
c4 PUSH CTOS 32 LDU 256 LDU ENDS // signature in_msg msg_seqno cs stored_seqno public_key
s3 s1 XCPU // signature in_msg public_key cs stored_seqno msg_seqno stored_seqno
EQUAL 33 THROWIFNOT // signature in_msg public_key cs stored_seqno
s0 s3 XCHG HASHSU // signature stored_seqno public_key cs hash
s0 s4 s2 XC2PU CHKSIGNU 34 THROWIFNOT // cs stored_seqno public_key
ACCEPT
s0 s2 XCHG // public_key stored_seqno cs
WHILE:<{
DUP SREFS // public_key stored_seqno cs _40
}>DO<{ // public_key stored_seqno cs
// 3 INT 35 LSHIFT# 3 INT RAWRESERVE // reserve all but 103 Grams from the balance
8 LDU LDREF s0 s2 XCHG // public_key stored_seqno cs _45 mode
SENDRAWMSG // public_key stored_seqno cs
}>
ENDS INC // public_key seqno'
NEWC 32 STU 256 STU ENDC c4 POP
)ABCD";
return fift::compile_asm(code).move_as_ok();
}
TEST(Tonlib, TestWallet) {
LOG(ERROR) << td::base64_encode(std_boc_serialize(get_test_wallet_source()).move_as_ok());
CHECK(get_test_wallet_source()->get_hash() == ton::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 = ton::TestWallet::get_init_state(pub_key);
auto init_message = ton::TestWallet::get_init_message(priv_key);
auto address = ton::GenericAccount::get_address(0, init_state);
CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32));
td::Ref<vm::Cell> res = ton::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());
fift_output.source_lookup.write_file("/main.fif", load_source("smartcont/wallet.fif")).ensure();
auto dest = block::StdAddress::parse("Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX").move_as_ok();
fift_output = fift::mem_run_fift(std::move(fift_output.source_lookup),
{"aba", "new-wallet", "Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "123",
"321", "-C", "TEST"})
.move_as_ok();
auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data;
auto gift_message = ton::GenericAccount::create_ext_message(
address, {}, ton::TestWallet::make_a_gift_message(priv_key, 123, 321000000000ll, "TEST", dest));
LOG(ERROR) << "-------";
vm::load_cell_slice(gift_message).print_rec(std::cerr);
LOG(ERROR) << "-------";
vm::load_cell_slice(vm::std_boc_deserialize(wallet_query).move_as_ok()).print_rec(std::cerr);
CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == gift_message->get_hash());
}
td::Ref<vm::Cell> get_wallet_source_fc() {
return fift::compile_asm(load_source("smartcont/wallet-code.fif"), "", false).move_as_ok();
}
TEST(Tonlib, Wallet) {
LOG(ERROR) << td::base64_encode(std_boc_serialize(get_wallet_source()).move_as_ok());
CHECK(get_wallet_source()->get_hash() == ton::Wallet::get_init_code()->get_hash());
auto fift_output = fift::mem_run_fift(load_source("smartcont/new-wallet-v2.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 = ton::Wallet::get_init_state(pub_key);
auto init_message = ton::Wallet::get_init_message(priv_key);
auto address = ton::GenericAccount::get_address(0, init_state);
CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32));
td::Ref<vm::Cell> res = ton::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());
fift_output.source_lookup.write_file("/main.fif", load_source("smartcont/wallet-v2.fif")).ensure();
class ZeroOsTime : public fift::OsTime {
public:
td::uint32 now() override {
return 0;
}
};
fift_output.source_lookup.set_os_time(std::make_unique<ZeroOsTime>());
auto dest = block::StdAddress::parse("Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX").move_as_ok();
fift_output =
fift::mem_run_fift(std::move(fift_output.source_lookup),
{"aba", "new-wallet", "Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "123", "321"})
.move_as_ok();
auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data;
auto gift_message = ton::GenericAccount::create_ext_message(
address, {}, ton::Wallet::make_a_gift_message(priv_key, 123, 60, 321000000000ll, "TESTv2", dest));
LOG(ERROR) << "-------";
vm::load_cell_slice(gift_message).print_rec(std::cerr);
LOG(ERROR) << "-------";
vm::load_cell_slice(vm::std_boc_deserialize(wallet_query).move_as_ok()).print_rec(std::cerr);
CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == gift_message->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 = ton::GenericAccount::create_ext_message(
ton::TestGiver::address(), {},
ton::TestGiver::make_a_gift_message(0, 1000000000ll * 6666 / 1000, "GIFT", 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());
}
class SimpleWallet : public ton::SmartContract {
public:
SimpleWallet(State state) : SmartContract(std::move(state)) {
}
const State& get_state() const {
return state_;
}
SimpleWallet* make_copy() const override {
return new SimpleWallet{state_};
}
static td::Ref<SimpleWallet> create_empty() {
return td::Ref<SimpleWallet>(true, State{ton::SmartContractCode::simple_wallet_ext(), {}});
}
static td::Ref<SimpleWallet> create(td::Ref<vm::Cell> data) {
return td::Ref<SimpleWallet>(true, State{ton::SmartContractCode::simple_wallet_ext(), std::move(data)});
}
static td::Ref<SimpleWallet> create_fast(td::Ref<vm::Cell> data) {
return td::Ref<SimpleWallet>(true, State{ton::SmartContractCode::simple_wallet(), std::move(data)});
}
td::int32 seqno() const {
auto res = run_get_method("seqno");
return res.stack.write().pop_smallint_range(1000000000);
}
td::Ref<vm::Cell> create_init_state(td::Slice public_key) const {
td::RefInt256 pk{true};
pk.write().import_bytes(public_key.ubegin(), public_key.size(), false);
auto res = run_get_method("create_init_state", {pk});
return res.stack.write().pop_cell();
}
td::Ref<vm::Cell> prepare_send_message(td::Ref<vm::Cell> msg, td::int8 mode = 3) const {
auto res = run_get_method("prepare_send_message", {td::make_refint(mode), msg});
return res.stack.write().pop_cell();
}
static td::Ref<vm::Cell> sign_message(vm::Ref<vm::Cell> body, const td::Ed25519::PrivateKey& pk) {
auto signature = pk.sign(body->get_hash().as_slice()).move_as_ok();
return vm::CellBuilder().store_bytes(signature.as_slice()).append_cellslice(vm::load_cell_slice(body)).finalize();
}
};
TEST(Smartcon, Simple) {
auto private_key = td::Ed25519::generate_private_key().move_as_ok();
auto public_key = private_key.get_public_key().move_as_ok().as_octet_string();
auto w_lib = SimpleWallet::create_empty();
auto init_data = w_lib->create_init_state(public_key);
auto w = SimpleWallet::create(init_data);
LOG(ERROR) << w->code_size();
auto fw = SimpleWallet::create_fast(init_data);
LOG(ERROR) << fw->code_size();
LOG(ERROR) << w->seqno();
for (int i = 0; i < 20; i++) {
auto msg = w->sign_message(w->prepare_send_message(vm::CellBuilder().finalize()), private_key);
w.write().send_external_message(msg);
fw.write().send_external_message(msg);
}
ASSERT_EQ(20, w->seqno());
CHECK(w->get_state().data->get_hash() == fw->get_state().data->get_hash());
}
namespace std { // ouch
bool operator<(const ton::MultisigWallet::Mask& a, const ton::MultisigWallet::Mask& b) {
for (size_t i = 0; i < a.size(); i++) {
if (a[i] != b[i]) {
return a[i] < b[i];
}
}
return false;
}
} // namespace std
TEST(Smartcon, Multisig) {
auto ms_lib = ton::MultisigWallet::create();
int n = 100;
int k = 99;
std::vector<td::Ed25519::PrivateKey> keys;
for (int i = 0; i < n; i++) {
keys.push_back(td::Ed25519::generate_private_key().move_as_ok());
}
auto init_state = ms_lib->create_init_data(
td::transform(keys, [](auto& key) { return key.get_public_key().ok().as_octet_string(); }), k);
auto ms = ton::MultisigWallet::create(init_state);
td::uint64 query_id = 123;
ton::MultisigWallet::QueryBuilder qb(query_id, vm::CellBuilder().finalize());
// first empty query (init)
CHECK(ms.write().send_external_message(vm::CellBuilder().finalize()).code == 0);
// first empty query
CHECK(ms.write().send_external_message(vm::CellBuilder().finalize()).code > 0);
for (int i = 0; i < 10; i++) {
auto query = qb.create(i, keys[i]);
auto ans = ms.write().send_external_message(query);
LOG(INFO) << "CODE: " << ans.code;
LOG(INFO) << "GAS: " << ans.gas_used;
}
for (int i = 0; i + 1 < 50; i++) {
qb.sign(i, keys[i]);
}
auto query = qb.create(49, keys[49]);
CHECK(ms->get_n_k() == std::make_pair(n, k));
auto ans = ms.write().send_external_message(query);
LOG(INFO) << "CODE: " << ans.code;
LOG(INFO) << "GAS: " << ans.gas_used;
CHECK(ans.success);
ASSERT_EQ(0, ms->processed(query_id));
CHECK(ms.write().send_external_message(query).code > 0);
ASSERT_EQ(0, ms->processed(query_id));
{
ton::MultisigWallet::QueryBuilder qb(query_id, vm::CellBuilder().finalize());
for (int i = 50; i + 1 < 100; i++) {
qb.sign(i, keys[i]);
}
query = qb.create(99, keys[99]);
}
ans = ms.write().send_external_message(query);
LOG(INFO) << "CODE: " << ans.code;
LOG(INFO) << "GAS: " << ans.gas_used;
ASSERT_EQ(-1, ms->processed(query_id));
}
TEST(Smartcont, MultisigStress) {
int n = 10;
int k = 5;
std::vector<td::Ed25519::PrivateKey> keys;
for (int i = 0; i < n; i++) {
keys.push_back(td::Ed25519::generate_private_key().move_as_ok());
}
auto public_keys = td::transform(keys, [](auto& key) { return key.get_public_key().ok().as_octet_string(); });
auto ms_lib = ton::MultisigWallet::create();
auto init_state_old =
ms_lib->create_init_data_fast(td::transform(public_keys, [](auto& key) { return key.copy(); }), k);
auto init_state = ms_lib->create_init_data(td::transform(public_keys, [](auto& key) { return key.copy(); }), k);
CHECK(init_state_old->get_hash() == init_state->get_hash());
auto ms = ton::MultisigWallet::create(init_state);
CHECK(ms->get_public_keys() == public_keys);
td::int32 now = 0;
td::int32 qid = 1;
using Mask = std::bitset<128>;
struct Query {
td::int64 id;
td::Ref<vm::Cell> message;
Mask signed_mask;
};
std::vector<Query> queries;
int max_queries = 300;
td::Random::Xorshift128plus rnd(123);
auto new_query = [&] {
if (qid > max_queries) {
return;
}
Query query;
query.id = (static_cast<td::int64>(now) << 32) | qid++;
query.message = vm::CellBuilder().store_bytes(td::rand_string('a', 'z', rnd.fast(0, 100))).finalize();
queries.push_back(std::move(query));
};
auto verify = [&] {
auto messages = ms->get_unsigned_messaged();
std::set<std::tuple<td::uint64, ton::MultisigWallet::Mask, std::string>> s;
std::set<std::tuple<td::uint64, ton::MultisigWallet::Mask, std::string>> t;
for (auto& m : messages) {
auto x = std::make_tuple(m.query_id, m.signed_by, m.message->get_hash().as_slice().str());
s.insert(std::move(x));
}
for (auto& q : queries) {
if (q.signed_mask.none()) {
continue;
}
t.insert(std::make_tuple(q.id, q.signed_mask, q.message->get_hash().as_slice().str()));
}
ASSERT_EQ(t.size(), s.size());
CHECK(s == t);
};
auto sign_query = [&](Query& query, Mask mask) {
auto qb = ton::MultisigWallet::QueryBuilder(query.id, query.message);
int first_i = -1;
for (int i = 0; i < (int)mask.size(); i++) {
if (mask.test(i)) {
if (first_i == -1) {
first_i = i;
} else {
qb.sign(i, keys[i]);
}
}
}
return qb.create(first_i, keys[first_i]);
};
auto send_signature = [&](td::Ref<vm::Cell> query) {
auto ans = ms.write().send_external_message(query);
LOG(ERROR) << "GAS: " << ans.gas_used;
return ans.code == 0;
};
auto is_ready = [&](Query& query) { return ms->processed(query.id) == -1; };
auto gen_query = [&](Query& query) {
auto x = rnd.fast(1, n);
Mask mask;
for (int t = 0; t < x; t++) {
mask.set(rnd() % n);
}
auto signature = sign_query(query, mask);
return std::make_pair(signature, mask);
};
auto rand_sign = [&] {
if (queries.empty()) {
return;
}
size_t query_i = rnd() % queries.size();
auto& query = queries[query_i];
Mask mask;
td::Ref<vm::Cell> signature;
std::tie(signature, mask) = gen_query(query);
if (false && rnd() % 6 == 0) {
Mask mask2;
td::Ref<vm::Cell> signature2;
std::tie(signature2, mask2) = gen_query(query);
for (int i = 0; i < (int)keys.size(); i++) {
if (mask[i]) {
signature = ms->merge_queries(std::move(signature), std::move(signature2));
break;
}
if (mask2[i]) {
signature = ms->merge_queries(std::move(signature2), std::move(signature));
break;
}
}
//signature = ms->merge_queries(std::move(signature), std::move(signature2));
mask |= mask2;
}
int got_cnt;
Mask got_cnt_bits;
std::tie(got_cnt, got_cnt_bits) = ms->check_query_signatures(signature);
CHECK(mask == got_cnt_bits);
bool expect_ok = true;
{
auto new_mask = mask & ~query.signed_mask;
expect_ok &= new_mask.any();
for (size_t i = 0; i < mask.size(); i++) {
if (mask[i]) {
expect_ok &= new_mask[i];
break;
}
}
}
ASSERT_EQ(expect_ok, send_signature(std::move(signature)));
if (expect_ok) {
query.signed_mask |= mask;
}
auto expect_is_ready = query.signed_mask.count() >= (size_t)k;
auto state = ms->get_query_state(query.id);
ASSERT_EQ(expect_is_ready, (state.state == ton::MultisigWallet::QueryState::Sent));
CHECK(expect_is_ready || state.mask == query.signed_mask);
ASSERT_EQ(expect_is_ready, is_ready(query));
if (expect_is_ready) {
queries.erase(queries.begin() + query_i);
}
verify();
};
td::RandomSteps steps({{rand_sign, 2}, {new_query, 1}});
while (!queries.empty() || qid <= max_queries) {
steps.step(rnd);
//LOG(ERROR) << ms->data_size();
}
LOG(INFO) << "Final code size: " << ms->code_size();
LOG(INFO) << "Final data size: " << ms->data_size();
}

1160
crypto/test/wycheproof.h Normal file

File diff suppressed because it is too large Load diff

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

@ -744,6 +744,9 @@ int VmState::run() {
} }
} while (!res); } while (!res);
// LOG(INFO) << "[EN] data cells: " << DataCell::get_total_data_cells(); // LOG(INFO) << "[EN] data cells: " << DataCell::get_total_data_cells();
if ((res | 1) == -1) {
commit();
}
return res; return res;
} }

View file

@ -154,7 +154,7 @@ struct ControlData {
class Continuation : public td::CntObject { class Continuation : public td::CntObject {
public: public:
virtual int jump(VmState* st) const & = 0; virtual int jump(VmState* st) const& = 0;
virtual int jump_w(VmState* st) &; virtual int jump_w(VmState* st) &;
virtual ControlData* get_cdata() { virtual ControlData* get_cdata() {
return 0; return 0;
@ -184,7 +184,7 @@ class QuitCont : public Continuation {
QuitCont(int _code = 0) : exit_code(_code) { QuitCont(int _code = 0) : exit_code(_code) {
} }
~QuitCont() override = default; ~QuitCont() override = default;
int jump(VmState* st) const & override { int jump(VmState* st) const& override {
return ~exit_code; return ~exit_code;
} }
}; };
@ -193,7 +193,7 @@ class ExcQuitCont : public Continuation {
public: public:
ExcQuitCont() = default; ExcQuitCont() = default;
~ExcQuitCont() override = default; ~ExcQuitCont() override = default;
int jump(VmState* st) const & override; int jump(VmState* st) const& override;
}; };
class PushIntCont : public Continuation { class PushIntCont : public Continuation {
@ -204,7 +204,7 @@ class PushIntCont : public Continuation {
PushIntCont(int val, Ref<Continuation> _next) : push_val(val), next(_next) { PushIntCont(int val, Ref<Continuation> _next) : push_val(val), next(_next) {
} }
~PushIntCont() override = default; ~PushIntCont() override = default;
int jump(VmState* st) const & override; int jump(VmState* st) const& override;
int jump_w(VmState* st) & override; int jump_w(VmState* st) & override;
}; };
@ -217,7 +217,7 @@ class RepeatCont : public Continuation {
: body(std::move(_body)), after(std::move(_after)), count(_count) { : body(std::move(_body)), after(std::move(_after)), count(_count) {
} }
~RepeatCont() override = default; ~RepeatCont() override = default;
int jump(VmState* st) const & override; int jump(VmState* st) const& override;
int jump_w(VmState* st) & override; int jump_w(VmState* st) & override;
}; };
@ -228,7 +228,7 @@ class AgainCont : public Continuation {
AgainCont(Ref<Continuation> _body) : body(std::move(_body)) { AgainCont(Ref<Continuation> _body) : body(std::move(_body)) {
} }
~AgainCont() override = default; ~AgainCont() override = default;
int jump(VmState* st) const & override; int jump(VmState* st) const& override;
int jump_w(VmState* st) & override; int jump_w(VmState* st) & override;
}; };
@ -239,7 +239,7 @@ class UntilCont : public Continuation {
UntilCont(Ref<Continuation> _body, Ref<Continuation> _after) : body(std::move(_body)), after(std::move(_after)) { UntilCont(Ref<Continuation> _body, Ref<Continuation> _after) : body(std::move(_body)), after(std::move(_after)) {
} }
~UntilCont() override = default; ~UntilCont() override = default;
int jump(VmState* st) const & override; int jump(VmState* st) const& override;
int jump_w(VmState* st) & override; int jump_w(VmState* st) & override;
}; };
@ -252,7 +252,7 @@ class WhileCont : public Continuation {
: cond(std::move(_cond)), body(std::move(_body)), after(std::move(_after)), chkcond(_chk) { : cond(std::move(_cond)), body(std::move(_body)), after(std::move(_after)), chkcond(_chk) {
} }
~WhileCont() override = default; ~WhileCont() override = default;
int jump(VmState* st) const & override; int jump(VmState* st) const& override;
int jump_w(VmState* st) & override; int jump_w(VmState* st) & override;
}; };
@ -268,7 +268,7 @@ class ArgContExt : public Continuation {
ArgContExt(const ArgContExt&) = default; ArgContExt(const ArgContExt&) = default;
ArgContExt(ArgContExt&&) = default; ArgContExt(ArgContExt&&) = default;
~ArgContExt() override = default; ~ArgContExt() override = default;
int jump(VmState* st) const & override; int jump(VmState* st) const& override;
int jump_w(VmState* st) & override; int jump_w(VmState* st) & override;
ControlData* get_cdata() override { ControlData* get_cdata() override {
return &data; return &data;
@ -303,7 +303,7 @@ class OrdCont : public Continuation {
td::CntObject* make_copy() const override { td::CntObject* make_copy() const override {
return new OrdCont{*this}; return new OrdCont{*this};
} }
int jump(VmState* st) const & override; int jump(VmState* st) const& override;
int jump_w(VmState* st) & override; int jump_w(VmState* st) & override;
ControlData* get_cdata() override { ControlData* get_cdata() override {
@ -321,7 +321,7 @@ class OrdCont : public Continuation {
Ref<Stack> get_stack_ref() const { Ref<Stack> get_stack_ref() const {
return data.stack; return data.stack;
} }
Ref<OrdCont> copy_ord() const & { Ref<OrdCont> copy_ord() const& {
return Ref<OrdCont>{true, *this}; return Ref<OrdCont>{true, *this};
} }
Ref<OrdCont> copy_ord() && { Ref<OrdCont> copy_ord() && {
@ -375,10 +375,16 @@ struct GasLimits {
} }
}; };
struct CommittedState {
Ref<vm::Cell> c4, c5;
bool committed{false};
};
class VmState final : public VmStateInterface { class VmState final : public VmStateInterface {
Ref<CellSlice> code; Ref<CellSlice> code;
Ref<Stack> stack; Ref<Stack> stack;
ControlRegs cr; ControlRegs cr;
CommittedState cstate;
int cp; int cp;
long long steps{0}; long long steps{0};
const DispatchTable* dispatch; const DispatchTable* dispatch;
@ -388,6 +394,8 @@ class VmState final : public VmStateInterface {
std::vector<Ref<Cell>> libraries; std::vector<Ref<Cell>> libraries;
int stack_trace{0}, debug_off{0}; int stack_trace{0}, debug_off{0};
bool chksig_always_succeed{false};
public: public:
static constexpr unsigned cell_load_gas_price = 100, cell_create_gas_price = 500, exception_gas_price = 50, static constexpr unsigned cell_load_gas_price = 100, cell_create_gas_price = 500, exception_gas_price = 50,
tuple_entry_gas_price = 1; tuple_entry_gas_price = 1;
@ -401,6 +409,10 @@ class VmState final : public VmStateInterface {
VmState(Ref<Cell> code_cell, Args&&... args) VmState(Ref<Cell> code_cell, Args&&... args)
: VmState(convert_code_cell(std::move(code_cell)), std::forward<Args>(args)...) { : VmState(convert_code_cell(std::move(code_cell)), std::forward<Args>(args)...) {
} }
VmState(const VmState&) = delete;
VmState(VmState&&) = delete;
VmState& operator=(const VmState&) = delete;
VmState& operator=(VmState&&) = delete;
bool set_gas_limits(long long _max, long long _limit, long long _credit = 0); bool set_gas_limits(long long _max, long long _limit, long long _credit = 0);
bool final_gas_ok() const { bool final_gas_ok() const {
return gas.final_ok(); return gas.final_ok();
@ -408,6 +420,12 @@ class VmState final : public VmStateInterface {
long long gas_consumed() const { long long gas_consumed() const {
return gas.gas_consumed(); return gas.gas_consumed();
} }
bool committed() const {
return cstate.committed;
}
const CommittedState& get_committed_state() const {
return cstate;
}
void consume_gas(long long amount) { void consume_gas(long long amount) {
gas.consume(amount); gas.consume(amount);
} }
@ -567,6 +585,18 @@ class VmState final : public VmStateInterface {
return cont->is_unique() ? cont.unique_write().jump_w(this) : cont->jump(this); return cont->is_unique() ? cont.unique_write().jump_w(this) : cont->jump(this);
} }
static Ref<CellSlice> convert_code_cell(Ref<Cell> code_cell); static Ref<CellSlice> convert_code_cell(Ref<Cell> code_cell);
void commit() {
cstate.c4 = cr.d[0];
cstate.c5 = cr.d[1];
cstate.committed = true;
}
void set_chksig_always_succeed(bool flag) {
chksig_always_succeed = flag;
}
bool get_chksig_always_succeed() const {
return chksig_always_succeed;
}
private: private:
void init_cregs(bool same_c3 = false, bool push_0 = true); void init_cregs(bool same_c3 = false, bool push_0 = true);

View file

@ -18,6 +18,8 @@
*/ */
#pragma once #pragma once
#include "td/utils/Status.h"
namespace vm { namespace vm {
enum class Excno : int { enum class Excno : int {
@ -95,4 +97,19 @@ struct VmVirtError {
struct VmFatal {}; struct VmFatal {};
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());
} catch (vm::VmVirtError error) {
return td::Status::Error(PSLICE() << "Got a vm virtualization exception: " << error.get_msg());
} catch (vm::VmNoGas error) {
return td::Status::Error(PSLICE() << "Got a vm no gas exception: " << error.get_msg());
}
}
#define TRY_VM(f) ::vm::try_f([&] { return f; })
} // namespace vm } // namespace vm

View file

@ -75,10 +75,17 @@ int exec_set_gas_limit(VmState* st) {
return exec_set_gas_generic(st, gas); return exec_set_gas_generic(st, gas);
} }
int exec_commit(VmState* st) {
VM_LOG(st) << "execute COMMIT";
st->commit();
return 0;
}
void register_basic_gas_ops(OpcodeTable& cp0) { void register_basic_gas_ops(OpcodeTable& cp0) {
using namespace std::placeholders; using namespace std::placeholders;
cp0.insert(OpcodeInstr::mksimple(0xf800, 16, "ACCEPT", exec_accept)) cp0.insert(OpcodeInstr::mksimple(0xf800, 16, "ACCEPT", exec_accept))
.insert(OpcodeInstr::mksimple(0xf801, 16, "SETGASLIMIT", exec_set_gas_limit)); .insert(OpcodeInstr::mksimple(0xf801, 16, "SETGASLIMIT", exec_set_gas_limit))
.insert(OpcodeInstr::mksimple(0xf80f, 16, "COMMIT", exec_commit));
} }
void register_ton_gas_ops(OpcodeTable& cp0) { void register_ton_gas_ops(OpcodeTable& cp0) {
@ -268,7 +275,7 @@ int exec_ed25519_check_signature(VmState* st, bool from_slice) {
} }
td::Ed25519::PublicKey pub_key{td::SecureString(td::Slice{key, 32})}; td::Ed25519::PublicKey pub_key{td::SecureString(td::Slice{key, 32})};
auto res = pub_key.verify_signature(td::Slice{data, data_len}, td::Slice{signature, 64}); auto res = pub_key.verify_signature(td::Slice{data, data_len}, td::Slice{signature, 64});
stack.push_bool(res.is_ok()); stack.push_bool(res.is_ok() || st->get_chksig_always_succeed());
return 0; return 0;
} }

View file

@ -2160,7 +2160,8 @@ Of the following primitives, only the first two are ``pure'' in the sense that t
\item {\tt F802} --- {\tt BUYGAS} ($x$ -- ), computes the amount of gas that can be bought for $x$ nanograms, and sets $g_l$ accordingly in the same way as {\tt SETGASLIMIT}. \item {\tt F802} --- {\tt BUYGAS} ($x$ -- ), computes the amount of gas that can be bought for $x$ nanograms, and sets $g_l$ accordingly in the same way as {\tt SETGASLIMIT}.
\item {\tt F804} --- {\tt GRAMTOGAS} ($x$ -- $g$), computes the amount of gas that can be bought for $x$ nanograms. If $x$ is negative, returns 0. If $g$ exceeds $2^{63}-1$, it is replaced with this value. \item {\tt F804} --- {\tt GRAMTOGAS} ($x$ -- $g$), computes the amount of gas that can be bought for $x$ nanograms. If $x$ is negative, returns 0. If $g$ exceeds $2^{63}-1$, it is replaced with this value.
\item {\tt F805} --- {\tt GASTOGRAM} ($g$ -- $x$), computes the price of $g$ gas in nanograms. \item {\tt F805} --- {\tt GASTOGRAM} ($g$ -- $x$), computes the price of $g$ gas in nanograms.
\item {\tt F806}--{\tt F80F} --- Reserved for gas-related primitives. \item {\tt F806}--{\tt F80E} --- Reserved for gas-related primitives.
\item {\tt F80F} --- {\tt COMMIT} ( -- ), commits the current state of registers {\tt c4} (``persistent data'') and {\tt c5} (``actions'') so that the current execution is considered ``successful'' with the saved values even if an exception is thrown later.
\end{itemize} \end{itemize}
\nxsubpoint\emb{Pseudo-random number generator primitives} \nxsubpoint\emb{Pseudo-random number generator primitives}

View file

@ -13,18 +13,18 @@ import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
/** /**
* Main class for interaction with the TDLib. * Main class for interaction with the tonlib.
*/ */
public final class Client implements Runnable { public final class Client implements Runnable {
static { static {
System.loadLibrary("native-lib"); System.loadLibrary("native-lib");
} }
/** /**
* Interface for handler for results of queries to TDLib and incoming updates from TDLib. * Interface for handler for results of queries to tonlib and incoming updates from tonlib.
*/ */
public interface ResultHandler { public interface ResultHandler {
/** /**
* Callback called on result of query to TDLib or incoming update from TDLib. * Callback called on result of query to tonlib or incoming update from tonlib.
* *
* @param object Result of query or update of type TonApi.Update about new events. * @param object Result of query or update of type TonApi.Update about new events.
*/ */
@ -46,9 +46,9 @@ public final class Client implements Runnable {
} }
/** /**
* Sends a request to the TDLib. * Sends a request to the tonlib.
* *
* @param query Object representing a query to the TDLib. * @param query Object representing a query to the tonlib.
* @param resultHandler Result handler with onResult method which will be called with result * @param resultHandler Result handler with onResult method which will be called with result
* of the query or with TonApi.Error as parameter. If it is null, nothing * of the query or with TonApi.Error as parameter. If it is null, nothing
* will be called. * will be called.
@ -80,9 +80,9 @@ public final class Client implements Runnable {
} }
/** /**
* Sends a request to the TDLib with an empty ExceptionHandler. * Sends a request to the tonlib with an empty ExceptionHandler.
* *
* @param query Object representing a query to the TDLib. * @param query Object representing a query to the tonlib.
* @param resultHandler Result handler with onResult method which will be called with result * @param resultHandler Result handler with onResult method which will be called with result
* of the query or with TonApi.Error as parameter. If it is null, then * of the query or with TonApi.Error as parameter. If it is null, then
* defaultExceptionHandler will be called. * defaultExceptionHandler will be called.
@ -93,9 +93,9 @@ public final class Client implements Runnable {
} }
/** /**
* Synchronously executes a TDLib request. Only a few marked accordingly requests can be executed synchronously. * Synchronously executes a tonlib request. Only a few marked accordingly requests can be executed synchronously.
* *
* @param query Object representing a query to the TDLib. * @param query Object representing a query to the tonlib.
* @return request result. * @return request result.
* @throws NullPointerException if query is null. * @throws NullPointerException if query is null.
*/ */
@ -107,10 +107,10 @@ public final class Client implements Runnable {
} }
/** /**
* Replaces handler for incoming updates from the TDLib. * Replaces handler for incoming updates from the tonlib.
* *
* @param updatesHandler Handler with onResult method which will be called for every incoming * @param updatesHandler Handler with onResult method which will be called for every incoming
* update from the TDLib. * update from the tonlib.
* @param exceptionHandler Exception handler with onException method which will be called on * @param exceptionHandler Exception handler with onException method which will be called on
* exception thrown from updatesHandler, if it is null, defaultExceptionHandler will be invoked. * exception thrown from updatesHandler, if it is null, defaultExceptionHandler will be invoked.
*/ */
@ -119,10 +119,10 @@ public final class Client implements Runnable {
} }
/** /**
* Replaces handler for incoming updates from the TDLib. Sets empty ExceptionHandler. * Replaces handler for incoming updates from the tonlib. Sets empty ExceptionHandler.
* *
* @param updatesHandler Handler with onResult method which will be called for every incoming * @param updatesHandler Handler with onResult method which will be called for every incoming
* update from the TDLib. * update from the tonlib.
*/ */
public void setUpdatesHandler(ResultHandler updatesHandler) { public void setUpdatesHandler(ResultHandler updatesHandler) {
setUpdatesHandler(updatesHandler, null); setUpdatesHandler(updatesHandler, null);
@ -157,7 +157,7 @@ public final class Client implements Runnable {
*/ */
public static Client create(ResultHandler updatesHandler, ExceptionHandler updatesExceptionHandler, ExceptionHandler defaultExceptionHandler) { public static Client create(ResultHandler updatesHandler, ExceptionHandler updatesExceptionHandler, ExceptionHandler defaultExceptionHandler) {
Client client = new Client(updatesHandler, updatesExceptionHandler, defaultExceptionHandler); Client client = new Client(updatesHandler, updatesExceptionHandler, defaultExceptionHandler);
new Thread(client, "TDLib thread").start(); new Thread(client, "tonlib thread").start();
return client; return client;
} }

View file

@ -13,18 +13,18 @@ import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
/** /**
* Main class for interaction with the TDLib. * Main class for interaction with the tonlib.
*/ */
public final class Client implements Runnable { public final class Client implements Runnable {
static { static {
System.loadLibrary("native-lib"); System.loadLibrary("native-lib");
} }
/** /**
* Interface for handler for results of queries to TDLib and incoming updates from TDLib. * Interface for handler for results of queries to tonlib and incoming updates from tonlib.
*/ */
public interface ResultHandler { public interface ResultHandler {
/** /**
* Callback called on result of query to TDLib or incoming update from TDLib. * Callback called on result of query to tonlib or incoming update from tonlib.
* *
* @param object Result of query or update of type TonApi.Update about new events. * @param object Result of query or update of type TonApi.Update about new events.
*/ */
@ -46,9 +46,9 @@ public final class Client implements Runnable {
} }
/** /**
* Sends a request to the TDLib. * Sends a request to the tonlib.
* *
* @param query Object representing a query to the TDLib. * @param query Object representing a query to the tonlib.
* @param resultHandler Result handler with onResult method which will be called with result * @param resultHandler Result handler with onResult method which will be called with result
* of the query or with TonApi.Error as parameter. If it is null, nothing * of the query or with TonApi.Error as parameter. If it is null, nothing
* will be called. * will be called.
@ -80,9 +80,9 @@ public final class Client implements Runnable {
} }
/** /**
* Sends a request to the TDLib with an empty ExceptionHandler. * Sends a request to the tonlib with an empty ExceptionHandler.
* *
* @param query Object representing a query to the TDLib. * @param query Object representing a query to the tonlib.
* @param resultHandler Result handler with onResult method which will be called with result * @param resultHandler Result handler with onResult method which will be called with result
* of the query or with TonApi.Error as parameter. If it is null, then * of the query or with TonApi.Error as parameter. If it is null, then
* defaultExceptionHandler will be called. * defaultExceptionHandler will be called.
@ -93,9 +93,9 @@ public final class Client implements Runnable {
} }
/** /**
* Synchronously executes a TDLib request. Only a few marked accordingly requests can be executed synchronously. * Synchronously executes a tonlib request. Only a few marked accordingly requests can be executed synchronously.
* *
* @param query Object representing a query to the TDLib. * @param query Object representing a query to the tonlib.
* @return request result. * @return request result.
* @throws NullPointerException if query is null. * @throws NullPointerException if query is null.
*/ */
@ -107,10 +107,10 @@ public final class Client implements Runnable {
} }
/** /**
* Replaces handler for incoming updates from the TDLib. * Replaces handler for incoming updates from the tonlib.
* *
* @param updatesHandler Handler with onResult method which will be called for every incoming * @param updatesHandler Handler with onResult method which will be called for every incoming
* update from the TDLib. * update from the tonlib.
* @param exceptionHandler Exception handler with onException method which will be called on * @param exceptionHandler Exception handler with onException method which will be called on
* exception thrown from updatesHandler, if it is null, defaultExceptionHandler will be invoked. * exception thrown from updatesHandler, if it is null, defaultExceptionHandler will be invoked.
*/ */
@ -119,10 +119,10 @@ public final class Client implements Runnable {
} }
/** /**
* Replaces handler for incoming updates from the TDLib. Sets empty ExceptionHandler. * Replaces handler for incoming updates from the tonlib. Sets empty ExceptionHandler.
* *
* @param updatesHandler Handler with onResult method which will be called for every incoming * @param updatesHandler Handler with onResult method which will be called for every incoming
* update from the TDLib. * update from the tonlib.
*/ */
public void setUpdatesHandler(ResultHandler updatesHandler) { public void setUpdatesHandler(ResultHandler updatesHandler) {
setUpdatesHandler(updatesHandler, null); setUpdatesHandler(updatesHandler, null);
@ -157,7 +157,7 @@ public final class Client implements Runnable {
*/ */
public static Client create(ResultHandler updatesHandler, ExceptionHandler updatesExceptionHandler, ExceptionHandler defaultExceptionHandler) { public static Client create(ResultHandler updatesHandler, ExceptionHandler updatesExceptionHandler, ExceptionHandler defaultExceptionHandler) {
Client client = new Client(updatesHandler, updatesExceptionHandler, defaultExceptionHandler); Client client = new Client(updatesHandler, updatesExceptionHandler, defaultExceptionHandler);
new Thread(client, "TDLib thread").start(); new Thread(client, "tonlib thread").start();
return client; return client;
} }

View file

@ -28,12 +28,12 @@
#include <iostream> #include <iostream>
#include <tonlib/tonlib_client_json.h> #include <tonlib/tonlib_client_json.h>
// Basic example of TONLib JSON interface usage. // Basic example of tonlib JSON interface usage.
// Native interface should be preferred instead in C++, so here is only an example of // Native interface should be preferred instead in C++, so here is only an example of
// the main event cycle, which should be essentially the same for all languages. // the main event cycle, which should be essentially the same for all languages.
int main() { int main() {
// disable TDLib logging // disable tonlib logging
void *client = tonlib_client_json_create(); void *client = tonlib_client_json_create();
// somehow share the client with other threads, which will be able to send requests via tonlib_client_json_send // somehow share the client with other threads, which will be able to send requests via tonlib_client_json_send

View file

@ -4,6 +4,7 @@
#include "tl-utils/lite-utils.hpp" #include "tl-utils/lite-utils.hpp"
#include "ton/lite-tl.hpp" #include "ton/lite-tl.hpp"
#include "td/utils/overloaded.h" #include "td/utils/overloaded.h"
#include "td/utils/Random.h"
using namespace std::literals::string_literals; using namespace std::literals::string_literals;
@ -74,4 +75,25 @@ td::Result<std::unique_ptr<block::BlockProofChain>> deserialize_proof_chain(
LOG(DEBUG) << "deserialized a BlkProofChain of " << chain->link_count() << " links"; LOG(DEBUG) << "deserialized a BlkProofChain of " << chain->link_count() << " links";
return std::move(chain); return std::move(chain);
} }
td::Ref<vm::Tuple> prepare_vm_c7(ton::UnixTime now, ton::LogicalTime lt, td::Ref<vm::CellSlice> my_addr,
const block::CurrencyCollection& balance) {
td::BitArray<256> rand_seed;
td::RefInt256 rand_seed_int{true};
td::Random::secure_bytes(rand_seed.as_slice());
if (!rand_seed_int.unique_write().import_bits(rand_seed.cbits(), 256, false)) {
return {};
}
auto tuple = vm::make_tuple_ref(td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea
td::make_refint(0), // actions:Integer
td::make_refint(0), // msgs_sent:Integer
td::make_refint(now), // unixtime:Integer
td::make_refint(lt), // block_lt:Integer
td::make_refint(lt), // trans_lt:Integer
std::move(rand_seed_int), // rand_seed:Integer
balance.as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)]
my_addr, // myself:MsgAddressInt
vm::StackEntry()); // global_config:(Maybe Cell) ] = SmartContractInfo;
LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string();
return vm::make_tuple_ref(std::move(tuple));
}
} // namespace liteclient } // namespace liteclient

View file

@ -7,4 +7,7 @@
namespace liteclient { namespace liteclient {
td::Result<std::unique_ptr<block::BlockProofChain>> deserialize_proof_chain( td::Result<std::unique_ptr<block::BlockProofChain>> deserialize_proof_chain(
ton::lite_api::object_ptr<ton::lite_api::liteServer_partialBlockProof> f); ton::lite_api::object_ptr<ton::lite_api::liteServer_partialBlockProof> f);
}
td::Ref<vm::Tuple> prepare_vm_c7(ton::UnixTime now, ton::LogicalTime lt, td::Ref<vm::CellSlice> my_addr,
const block::CurrencyCollection& balance);
} // namespace liteclient

View file

@ -1223,28 +1223,6 @@ void TestNode::got_account_state(ton::BlockIdExt ref_blk, ton::BlockIdExt blk, t
} }
} }
Ref<vm::Tuple> TestNode::prepare_vm_c7(ton::UnixTime now, ton::LogicalTime lt, Ref<vm::CellSlice> my_addr,
const block::CurrencyCollection& balance) const {
td::BitArray<256> rand_seed;
td::RefInt256 rand_seed_int{true};
prng::rand_gen().rand_bytes(rand_seed.data(), 32);
if (!rand_seed_int.unique_write().import_bits(rand_seed.cbits(), 256, false)) {
return {};
}
auto tuple = vm::make_tuple_ref(td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea
td::make_refint(0), // actions:Integer
td::make_refint(0), // msgs_sent:Integer
td::make_refint(now), // unixtime:Integer
td::make_refint(lt), // block_lt:Integer
td::make_refint(lt), // trans_lt:Integer
std::move(rand_seed_int), // rand_seed:Integer
balance.as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)]
my_addr, // myself:MsgAddressInt
vm::StackEntry()); // global_config:(Maybe Cell) ] = SmartContractInfo;
LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string();
return vm::make_tuple_ref(std::move(tuple));
}
void TestNode::run_smc_method(ton::BlockIdExt ref_blk, ton::BlockIdExt blk, ton::BlockIdExt shard_blk, void TestNode::run_smc_method(ton::BlockIdExt ref_blk, ton::BlockIdExt blk, ton::BlockIdExt shard_blk,
td::BufferSlice shard_proof, td::BufferSlice proof, td::BufferSlice state, td::BufferSlice shard_proof, td::BufferSlice proof, td::BufferSlice state,
ton::WorkchainId workchain, ton::StdSmcAddress addr, std::string method, ton::WorkchainId workchain, ton::StdSmcAddress addr, std::string method,
@ -1309,7 +1287,7 @@ void TestNode::run_smc_method(ton::BlockIdExt ref_blk, ton::BlockIdExt blk, ton:
vm::GasLimits gas{gas_limit}; vm::GasLimits gas{gas_limit};
LOG(DEBUG) << "creating VM"; LOG(DEBUG) << "creating VM";
vm::VmState vm{code, std::move(stack), gas, 1, data, vm::VmLog()}; vm::VmState vm{code, std::move(stack), gas, 1, data, vm::VmLog()};
vm.set_c7(prepare_vm_c7(info.gen_utime, info.gen_lt, acc.addr, balance)); // tuple with SmartContractInfo vm.set_c7(liteclient::prepare_vm_c7(info.gen_utime, info.gen_lt, acc.addr, balance)); // tuple with SmartContractInfo
// vm.incr_stack_trace(1); // enable stack dump after each step // vm.incr_stack_trace(1); // enable stack dump after each step
LOG(INFO) << "starting VM to run method `" << method << "` (" << method_id << ") of smart contract " << workchain LOG(INFO) << "starting VM to run method `" << method << "` (" << method_id << ") of smart contract " << workchain
<< ":" << addr.to_hex(); << ":" << addr.to_hex();

View file

@ -119,8 +119,6 @@ class TestNode : public td::actor::Actor {
td::BufferSlice shard_proof, td::BufferSlice proof, td::BufferSlice state, td::BufferSlice shard_proof, td::BufferSlice proof, td::BufferSlice state,
ton::WorkchainId workchain, ton::StdSmcAddress addr, std::string method, ton::WorkchainId workchain, ton::StdSmcAddress addr, std::string method,
std::vector<vm::StackEntry> params); std::vector<vm::StackEntry> params);
Ref<vm::Tuple> prepare_vm_c7(ton::UnixTime now, ton::LogicalTime lt, Ref<vm::CellSlice> my_addr,
const block::CurrencyCollection& balance) const;
bool get_all_shards(bool use_last = true, ton::BlockIdExt blkid = {}); bool get_all_shards(bool use_last = true, ton::BlockIdExt blkid = {});
void got_all_shards(ton::BlockIdExt blk, td::BufferSlice proof, td::BufferSlice data); void got_all_shards(ton::BlockIdExt blk, td::BufferSlice proof, td::BufferSlice data);
bool get_config_params(ton::BlockIdExt blkid, int mode = 0, std::string filename = ""); bool get_config_params(ton::BlockIdExt blkid, int mode = 0, std::string filename = "");

View file

@ -1145,6 +1145,273 @@ void run_queue_bench(int n, int m) {
#endif #endif
} }
struct Sem {
public:
void post() {
if (++cnt_ == 0) {
{
std::unique_lock<std::mutex> lk(mutex_);
}
cnd_.notify_one();
}
}
void wait(int cnt = 1) {
auto was = cnt_.fetch_sub(cnt);
if (was >= cnt) {
return;
}
std::unique_lock<std::mutex> lk(mutex_);
cnd_.wait(lk, [&] { return cnt_ >= 0; });
}
private:
std::mutex mutex_;
std::condition_variable cnd_;
std::atomic<int> cnt_{0};
};
class ChainedSpawn : public td::Benchmark {
public:
ChainedSpawn(bool use_io) : use_io_(use_io) {
}
std::string get_description() const {
return PSTRING() << "Chained create_actor use_io(" << use_io_ << ")";
}
void run(int n) {
class Task : public td::actor::Actor {
public:
Task(int n, Sem *sem) : n_(n), sem_(sem) {
}
void start_up() override {
if (n_ == 0) {
sem_->post();
} else {
td::actor::create_actor<Task>("Task", n_ - 1, sem_).release();
}
stop();
};
private:
int n_;
Sem *sem_{nullptr};
};
td::actor::Scheduler scheduler{{8}};
auto sch = td::thread([&] { scheduler.run(); });
Sem sem;
scheduler.run_in_context_external([&] {
for (int i = 0; i < n; i++) {
td::actor::create_actor<Task>(td::actor::ActorOptions().with_name("Task").with_poll(use_io_), 1000, &sem)
.release();
sem.wait();
}
td::actor::SchedulerContext::get()->stop();
});
sch.join();
}
private:
bool use_io_{false};
};
class ChainedSpawnInplace : public td::Benchmark {
public:
ChainedSpawnInplace(bool use_io) : use_io_(use_io) {
}
std::string get_description() const {
return PSTRING() << "Chained send_signal(self) use_io(" << use_io_ << ")";
}
void run(int n) {
class Task : public td::actor::Actor {
public:
Task(int n, Sem *sem) : n_(n), sem_(sem) {
}
void loop() override {
if (n_ == 0) {
sem_->post();
stop();
} else {
n_--;
send_signals(actor_id(this), td::actor::ActorSignals::wakeup());
}
};
private:
int n_;
Sem *sem_;
};
td::actor::Scheduler scheduler{{8}};
auto sch = td::thread([&] { scheduler.run(); });
Sem sem;
scheduler.run_in_context_external([&] {
for (int i = 0; i < n; i++) {
td::actor::create_actor<Task>(td::actor::ActorOptions().with_name("Task").with_poll(use_io_), 1000, &sem)
.release();
sem.wait();
}
td::actor::SchedulerContext::get()->stop();
});
sch.join();
}
private:
bool use_io_{false};
};
class PingPong : public td::Benchmark {
public:
PingPong(bool use_io) : use_io_(use_io) {
}
std::string get_description() const {
return PSTRING() << "PingPong use_io(" << use_io_ << ")";
}
void run(int n) {
if (n < 3) {
n = 3;
}
class Task : public td::actor::Actor {
public:
explicit Task(Sem *sem) : sem_(sem) {
}
void set_peer(td::actor::ActorId<Task> peer) {
peer_ = peer;
}
void ping(int n) {
if (n < 0) {
sem_->post();
stop();
}
send_closure(peer_, &Task::ping, n - 1);
}
private:
td::actor::ActorId<Task> peer_;
Sem *sem_;
};
td::actor::Scheduler scheduler{{8}};
auto sch = td::thread([&] { scheduler.run(); });
Sem sem;
scheduler.run_in_context_external([&] {
for (int i = 0; i < n; i++) {
auto a = td::actor::create_actor<Task>(td::actor::ActorOptions().with_name("Task").with_poll(use_io_), &sem)
.release();
auto b = td::actor::create_actor<Task>(td::actor::ActorOptions().with_name("Task").with_poll(use_io_), &sem)
.release();
send_closure(a, &Task::set_peer, b);
send_closure(b, &Task::set_peer, a);
send_closure(a, &Task::ping, 1000);
sem.wait(2);
}
td::actor::SchedulerContext::get()->stop();
});
sch.join();
}
private:
bool use_io_{false};
};
class SpawnMany : public td::Benchmark {
public:
SpawnMany(bool use_io) : use_io_(use_io) {
}
std::string get_description() const {
return PSTRING() << "Spawn many use_io(" << use_io_ << ")";
}
void run(int n) {
class Task : public td::actor::Actor {
public:
Task(Sem *sem) : sem_(sem) {
}
void start_up() override {
sem_->post();
stop();
};
private:
Sem *sem_;
};
td::actor::Scheduler scheduler{{8}};
Sem sem;
auto sch = td::thread([&] { scheduler.run(); });
scheduler.run_in_context_external([&] {
for (int i = 0; i < n; i++) {
int spawn_cnt = 10000;
for (int j = 0; j < spawn_cnt; j++) {
td::actor::create_actor<Task>(td::actor::ActorOptions().with_name("Task").with_poll(use_io_), &sem).release();
}
sem.wait(spawn_cnt);
}
td::actor::SchedulerContext::get()->stop();
});
sch.join();
}
private:
bool use_io_{false};
};
class YieldMany : public td::Benchmark {
public:
YieldMany(bool use_io) : use_io_(use_io) {
}
std::string get_description() const {
return PSTRING() << "Yield many use_io(" << use_io_ << ")";
}
void run(int n) {
int num_yield = 1000;
unsigned tasks_per_cpu = 50;
unsigned cpu_n = td::thread::hardware_concurrency();
class Task : public td::actor::Actor {
public:
explicit Task(int n, Sem *sem) : n_(n), sem_(sem) {
}
void loop() override {
if (n_ == 0) {
sem_->post();
stop();
} else {
n_--;
yield();
}
};
private:
int n_;
Sem *sem_;
};
td::actor::Scheduler scheduler{{cpu_n}};
auto sch = td::thread([&] { scheduler.run(); });
unsigned tasks = tasks_per_cpu * cpu_n;
Sem sem;
scheduler.run_in_context_external([&] {
for (int i = 0; i < n; i++) {
for (unsigned j = 0; j < tasks; j++) {
td::actor::create_actor<Task>(td::actor::ActorOptions().with_name("Task").with_poll(use_io_), num_yield, &sem)
.release();
}
sem.wait(tasks);
}
});
scheduler.run_in_context_external([&] { td::actor::SchedulerContext::get()->stop(); });
sch.join();
}
private:
bool use_io_{false};
};
int main(int argc, char **argv) { int main(int argc, char **argv) {
if (argc > 1) { if (argc > 1) {
if (argv[1][0] == 'a') { if (argv[1][0] == 'a') {
@ -1159,6 +1426,18 @@ int main(int argc, char **argv) {
return 0; return 0;
} }
bench(YieldMany(false));
bench(YieldMany(true));
bench(SpawnMany(false));
bench(SpawnMany(true));
bench(PingPong(false));
bench(PingPong(true));
bench(ChainedSpawnInplace(false));
bench(ChainedSpawnInplace(true));
bench(ChainedSpawn(false));
bench(ChainedSpawn(true));
return 0;
bench(ActorDummyQuery()); bench(ActorDummyQuery());
bench(ActorExecutorBenchmark()); bench(ActorExecutorBenchmark());
bench(ActorSignalQuery()); bench(ActorSignalQuery());

View file

@ -225,6 +225,7 @@ class Promise {
promise_->set_error(std::move(error)); promise_->set_error(std::move(error));
promise_.reset(); promise_.reset();
} }
void set_result(Result<T> &&result) { void set_result(Result<T> &&result) {
if (!promise_) { if (!promise_) {
return; return;
@ -260,6 +261,28 @@ class Promise {
explicit operator bool() { explicit operator bool() {
return static_cast<bool>(promise_); return static_cast<bool>(promise_);
} }
template <class V, class F>
auto do_wrap(V &&value, F &&func) {
if (value.is_ok()) {
set_result(func(value.move_as_ok()));
} else {
set_error(value.move_as_error());
}
}
template <class F>
auto do_wrap(td::Status status, F &&func) {
set_error(std::move(status));
}
template <class F>
auto wrap(F &&func) {
return [promise = std::move(*this), func = std::move(func)](auto &&res) mutable {
promise.do_wrap(std::move(res), std::move(func));
};
}
template <class... ArgsT>
auto send_closure(ArgsT &&... args);
private: private:
std::unique_ptr<PromiseInterface<T>> promise_; std::unique_ptr<PromiseInterface<T>> promise_;

View file

@ -162,4 +162,29 @@ void send_signals_later(ActorIdT &&actor_id, ActorSignals signals) {
detail::send_signals_later(id.as_actor_ref(), signals); detail::send_signals_later(id.as_actor_ref(), signals);
} }
} // namespace actor } // namespace actor
class SendClosure {
public:
template <class... ArgsT>
void operator()(ArgsT &&... args) const {
td::actor::send_closure(std::forward<ArgsT>(args)...);
}
};
template <class T>
template <class... ArgsT>
auto Promise<T>::send_closure(ArgsT &&... args) {
return [promise = std::move(*this), t = std::make_tuple(std::forward<ArgsT>(args)...)](auto &&r_res) mutable {
TRY_RESULT_PROMISE(promise, res, std::move(r_res));
td::call_tuple(SendClosure(), std::tuple_cat(std::move(t), std::make_tuple(std::move(res), std::move(promise))));
};
}
template <class... ArgsT>
auto promise_send_closure(ArgsT &&... args) {
return [t = std::make_tuple(std::forward<ArgsT>(args)...)](auto &&res) mutable {
td::call_tuple(SendClosure(), std::tuple_cat(std::move(t), std::make_tuple(std::move(res))));
};
}
} // namespace td } // namespace td

View file

@ -73,5 +73,57 @@ void TcpListener::loop() {
return stop(); return stop();
} }
} }
TcpInfiniteListener::TcpInfiniteListener(int32 port, std::unique_ptr<TcpListener::Callback> callback)
: port_(port), callback_(std::move(callback)) {
}
void TcpInfiniteListener::start_up() {
loop();
}
void TcpInfiniteListener::hangup() {
close_flag_ = true;
tcp_listener_.reset();
if (refcnt_ == 0) {
stop();
}
}
void TcpInfiniteListener::loop() {
if (!tcp_listener_.empty()) {
return;
}
class Callback : public TcpListener::Callback {
public:
Callback(actor::ActorShared<TcpInfiniteListener> parent) : parent_(std::move(parent)) {
}
void accept(SocketFd fd) override {
actor::send_closure(parent_, &TcpInfiniteListener::accept, std::move(fd));
}
private:
actor::ActorShared<TcpInfiniteListener> parent_;
};
refcnt_++;
tcp_listener_ = actor::create_actor<TcpListener>(
actor::ActorOptions().with_name(PSLICE() << "TcpListener" << tag("port", port_)).with_poll(), port_,
std::make_unique<Callback>(actor_shared(this)));
}
void TcpInfiniteListener::accept(SocketFd fd) {
callback_->accept(std::move(fd));
}
void TcpInfiniteListener::hangup_shared() {
refcnt_--;
tcp_listener_.reset();
if (close_flag_) {
if (refcnt_ == 0) {
stop();
}
} else {
alarm_timestamp() = Timestamp::in(5 /*5 seconds*/);
}
}
} // namespace td } // namespace td

View file

@ -49,4 +49,23 @@ class TcpListener : public td::actor::Actor, private td::ObserverBase {
void loop() override; void loop() override;
}; };
class TcpInfiniteListener : public actor::Actor {
public:
TcpInfiniteListener(int32 port, std::unique_ptr<TcpListener::Callback> callback);
private:
int32 port_;
std::unique_ptr<TcpListener::Callback> callback_;
actor::ActorOwn<TcpListener> tcp_listener_;
int32 refcnt_{0};
bool close_flag_{false};
void start_up() override;
void hangup() override;
void loop() override;
void accept(SocketFd fd);
void hangup_shared() override;
};
} // namespace td } // namespace td

View file

@ -130,70 +130,6 @@ void UdpServerImpl::hangup_shared() {
stop(); stop();
} }
class TcpInfiniteListener : public actor::Actor {
public:
TcpInfiniteListener(int32 port, std::unique_ptr<TcpListener::Callback> callback)
: port_(port), callback_(std::move(callback)) {
}
private:
int32 port_;
std::unique_ptr<TcpListener::Callback> callback_;
actor::ActorOwn<TcpListener> tcp_listener_;
int32 refcnt_{0};
bool close_flag_{false};
void start_up() override {
loop();
}
void hangup() override {
close_flag_ = true;
tcp_listener_.reset();
if (refcnt_ == 0) {
stop();
}
}
void loop() override {
if (!tcp_listener_.empty()) {
return;
}
class Callback : public TcpListener::Callback {
public:
Callback(actor::ActorShared<TcpInfiniteListener> parent) : parent_(std::move(parent)) {
}
void accept(SocketFd fd) override {
actor::send_closure(parent_, &TcpInfiniteListener::accept, std::move(fd));
}
private:
actor::ActorShared<TcpInfiniteListener> parent_;
};
VLOG(udp_server) << "Create listener";
refcnt_++;
tcp_listener_ = actor::create_actor<TcpListener>(
actor::ActorOptions().with_name(PSLICE() << "TcpListener" << tag("port", port_)).with_poll(), port_,
std::make_unique<Callback>(actor_shared(this)));
}
void accept(SocketFd fd) {
callback_->accept(std::move(fd));
}
void hangup_shared() override {
refcnt_--;
tcp_listener_.reset();
if (close_flag_) {
if (refcnt_ == 0) {
stop();
}
} else {
alarm_timestamp() = Timestamp::in(5 /*5 seconds*/);
}
}
};
class TcpClient : public td::actor::Actor, td::ObserverBase { class TcpClient : public td::actor::Actor, td::ObserverBase {
public: public:
class Callback { class Callback {

View file

@ -48,6 +48,15 @@
} \ } \
} }
#define TRY_STATUS_PROMISE(promise_name, status) \
{ \
auto try_status = (status); \
if (try_status.is_error()) { \
promise_name.set_error(std::move(try_status)); \
return; \
} \
}
#define TRY_RESULT(name, result) TRY_RESULT_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result) #define TRY_RESULT(name, result) TRY_RESULT_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result)
#define TRY_RESULT_PROMISE(promise_name, name, result) \ #define TRY_RESULT_PROMISE(promise_name, name, result) \
@ -309,6 +318,11 @@ class Status {
return std::move(*this); return std::move(*this);
} }
Auto move_as_ok() {
UNREACHABLE();
return {};
}
Status move_as_error_prefix(const Status &status) const TD_WARN_UNUSED_RESULT { Status move_as_error_prefix(const Status &status) const TD_WARN_UNUSED_RESULT {
return status.move_as_error_suffix(message()); return status.move_as_error_suffix(message());
} }

View file

@ -128,7 +128,7 @@ auto invoke(F &&f,
} }
template <class F, class... Args, std::size_t... S> template <class F, class... Args, std::size_t... S>
auto call_tuple_impl(F &func, std::tuple<Args...> &&tuple, IntSeq<S...>) { auto call_tuple_impl(F &&func, std::tuple<Args...> &&tuple, IntSeq<S...>) {
return func(std::forward<Args>(std::get<S>(tuple))...); return func(std::forward<Args>(std::get<S>(tuple))...);
} }
@ -163,7 +163,7 @@ class LogicAnd {
}; };
template <class F, class... Args> template <class F, class... Args>
auto call_tuple(F &func, std::tuple<Args...> &&tuple) { auto call_tuple(F &&func, std::tuple<Args...> &&tuple) {
return detail::call_tuple_impl(func, std::move(tuple), detail::IntRange<sizeof...(Args)>()); return detail::call_tuple_impl(func, std::move(tuple), detail::IntRange<sizeof...(Args)>());
} }

View file

@ -38,6 +38,34 @@
namespace td { namespace td {
class RandomSteps {
public:
struct Step {
std::function<void()> func;
td::uint32 weight;
};
RandomSteps(std::vector<Step> steps) : steps_(std::move(steps)) {
for (auto &step : steps_) {
steps_sum_ += step.weight;
}
}
template <class Random>
void step(Random &rnd) {
auto w = rnd() % steps_sum_;
for (auto &step : steps_) {
if (w < step.weight) {
step.func();
break;
}
w -= step.weight;
}
}
private:
std::vector<Step> steps_;
td::int32 steps_sum_ = 0;
};
class RegressionTester { class RegressionTester {
public: public:
virtual ~RegressionTester() = default; virtual ~RegressionTester() = default;

View file

@ -128,7 +128,7 @@ class JavadocTlDocumentationGenerator extends TlDocumentationGenerator
$this->addDocumentation('public class TdApi {', <<<EOT $this->addDocumentation('public class TdApi {', <<<EOT
$nullable_type_import/** $nullable_type_import/**
* This class contains as static nested classes all other TDLib interface * This class contains as static nested classes all other tonlib interface
* type-classes and function-classes. * type-classes and function-classes.
* <p> * <p>
* It has no inner classes, functions or public members. * It has no inner classes, functions or public members.
@ -138,7 +138,7 @@ EOT
$this->addDocumentation(' public abstract static class Object {', <<<EOT $this->addDocumentation(' public abstract static class Object {', <<<EOT
/** /**
* This class is a base class for all TDLib interface classes. * This class is a base class for all tonlib interface classes.
*/ */
EOT EOT
); );
@ -159,7 +159,7 @@ EOT
$this->addDocumentation(' public abstract static class Function extends Object {', <<<EOT $this->addDocumentation(' public abstract static class Function extends Object {', <<<EOT
/** /**
* This class is a base class for all TDLib interface function-classes. * This class is a base class for all tonlib interface function-classes.
*/ */
EOT EOT
); );

View file

@ -411,13 +411,14 @@ db.root.key.config = db.root.Key;
db.celldb.value block_id:tonNode.blockIdExt prev:int256 next:int256 root_hash:int256 = db.celldb.Value; db.celldb.value block_id:tonNode.blockIdExt prev:int256 next:int256 root_hash:int256 = db.celldb.Value;
db.celldb.key.value hash:int256 = db.celldb.key.Value; db.celldb.key.value hash:int256 = db.celldb.key.Value;
db.block.info id:tonNode.blockIdExt flags:# prev_left:flags.1?tonNode.blockIdExt db.block.info#4ac6e727 id:tonNode.blockIdExt flags:# prev_left:flags.1?tonNode.blockIdExt
prev_right:flags.2?tonNode.blockIdExt prev_right:flags.2?tonNode.blockIdExt
next_left:flags.3?tonNode.blockIdExt next_left:flags.3?tonNode.blockIdExt
next_right:flags.4?tonNode.blockIdExt next_right:flags.4?tonNode.blockIdExt
lt:flags.13?long lt:flags.13?long
ts:flags.14?int ts:flags.14?int
state:flags.17?int256 = db.block.Info; state:flags.17?int256 = db.block.Info;
db.block.packedInfo id:tonNode.blockIdExt unixtime:int offset:long = db.block.Info;
db.block.archivedInfo id:tonNode.blockIdExt flags:# next:flags.0?tonNode.blockIdExt = db.block.Info; db.block.archivedInfo id:tonNode.blockIdExt flags:# next:flags.0?tonNode.blockIdExt = db.block.Info;
db.blockdb.value next:tonNode.blockIdExt data:bytes = db.blockdb.Value; db.blockdb.value next:tonNode.blockIdExt data:bytes = db.blockdb.Value;
@ -436,6 +437,7 @@ db.filedb.key.proof block_id:tonNode.blockIdExt = db.filedb.Key;
db.filedb.key.proofLink block_id:tonNode.blockIdExt = db.filedb.Key; db.filedb.key.proofLink block_id:tonNode.blockIdExt = db.filedb.Key;
db.filedb.key.signatures block_id:tonNode.blockIdExt = db.filedb.Key; db.filedb.key.signatures block_id:tonNode.blockIdExt = db.filedb.Key;
db.filedb.key.candidate id:db.candidate.id = db.filedb.Key; db.filedb.key.candidate id:db.candidate.id = db.filedb.Key;
db.filedb.key.blockInfo block_id:tonNode.blockIdExt = db.filedb.Key;
db.filedb.value key:db.filedb.Key prev:int256 next:int256 file_hash:int256 = db.filedb.Value; db.filedb.value key:db.filedb.Key prev:int256 next:int256 file_hash:int256 = db.filedb.Value;
@ -462,6 +464,13 @@ db.lt.desc.value first_idx:int last_idx:int last_seqno:int last_lt:long last_ts:
db.lt.shard.value workchain:int shard:long = db.lt.shard.Value; db.lt.shard.value workchain:int shard:long = db.lt.shard.Value;
db.lt.status.value total_shards:int = db.lt.status.Value; db.lt.status.value total_shards:int = db.lt.status.Value;
db.archive.index.key = db.archive.Key;
db.archive.package.key unixtime:int key:Bool = db.archive.Key;
db.archive.index.value packages:(vector int) key_packages:(vector int) = db.archive.index.Value;
db.archive.package.firstBlock workchain:int shard:long seqno:int lt:long = db.archive.package.FirstBlock;
db.archive.package.value unixtime:int key:Bool firstblocks:(vector db.archive.package.firstBlock) deleted:Bool = db.archive.package.Value;
---functions--- ---functions---
---types--- ---types---

Binary file not shown.

View file

@ -24,7 +24,8 @@ config config:string blockchain_name:string use_callbacks_for_network:Bool ignor
options config:config keystore_type:KeyStoreType = Options; options config:config keystore_type:KeyStoreType = Options;
key public_key:string secret:secureBytes = Key; key public_key:string secret:secureBytes = Key;
inputKey key:key local_password:secureBytes = InputKey; inputKeyRegular key:key local_password:secureBytes = InputKey;
inputKeyFake = InputKey;
exportedKey word_list:vector<secureString> = ExportedKey; exportedKey word_list:vector<secureString> = ExportedKey;
exportedPemKey pem:secureString = ExportedPemKey; exportedPemKey pem:secureString = ExportedPemKey;
exportedEncryptedKey data:secureBytes = ExportedEncryptedKey; exportedEncryptedKey data:secureBytes = ExportedEncryptedKey;
@ -68,29 +69,39 @@ sendGramsResult sent_until:int53 body_hash:bytes = SendGramsResult;
syncStateDone = SyncState; syncStateDone = SyncState;
syncStateInProgress from_seqno:int32 to_seqno:int32 current_seqno:int32 = SyncState; syncStateInProgress from_seqno:int32 to_seqno:int32 current_seqno:int32 = SyncState;
fees in_fwd_fee:int53 storage_fee:int53 gas_fee:int53 fwd_fee:int53= Fees;
query.fees source_fees:fees destination_fees:fees = query.Fees;
query.info id:int53 valid_until:int53 body_hash:bytes = query.Info;
smc.info id:int53 = smc.Info;
updateSendLiteServerQuery id:int64 data:bytes = Update; updateSendLiteServerQuery id:int64 data:bytes = Update;
updateSyncState sync_state:SyncState = Update; updateSyncState sync_state:SyncState = Update;
//@class LogStream @description Describes a stream to which TDLib internal log is written //@class LogStream @description Describes a stream to which tonlib internal log is written
//@description The log is written to stderr or an OS specific log //@description The log is written to stderr or an OS specific log
logStreamDefault = LogStream; logStreamDefault = LogStream;
//@description The log is written to a file @path Path to the file to where the internal TDLib log will be written @max_file_size Maximum size of the file to where the internal TDLib log is written before the file will be auto-rotated //@description The log is written to a file @path Path to the file to where the internal tonlib log will be written @max_file_size Maximum size of the file to where the internal tonlib log is written before the file will be auto-rotated
logStreamFile path:string max_file_size:int53 = LogStream; logStreamFile path:string max_file_size:int53 = LogStream;
//@description The log is written nowhere //@description The log is written nowhere
logStreamEmpty = LogStream; logStreamEmpty = LogStream;
//@description Contains a TDLib internal log verbosity level @verbosity_level Log verbosity level //@description Contains a tonlib internal log verbosity level @verbosity_level Log verbosity level
logVerbosityLevel verbosity_level:int32 = LogVerbosityLevel; logVerbosityLevel verbosity_level:int32 = LogVerbosityLevel;
//@description Contains a list of available TDLib internal log tags @tags List of log tags //@description Contains a list of available tonlib internal log tags @tags List of log tags
logTags tags:vector<string> = LogTags; logTags tags:vector<string> = LogTags;
data bytes:secureBytes = Data; data bytes:secureBytes = Data;
tvm.cell bytes:string = tvm.Cell;
liteServer.info now:int53 version:int32 capabilities:int64 = liteServer.Info;
---functions--- ---functions---
init options:options = Ok; init options:options = Ok;
@ -101,13 +112,13 @@ options.setConfig config:config = Ok;
createNewKey local_password:secureBytes mnemonic_password:secureBytes random_extra_seed:secureBytes = Key; createNewKey local_password:secureBytes mnemonic_password:secureBytes random_extra_seed:secureBytes = Key;
deleteKey key:key = Ok; deleteKey key:key = Ok;
deleteAllKeys = Ok; deleteAllKeys = Ok;
exportKey input_key:inputKey = ExportedKey; exportKey input_key:InputKey = ExportedKey;
exportPemKey input_key:inputKey key_password:secureBytes = ExportedPemKey; exportPemKey input_key:InputKey key_password:secureBytes = ExportedPemKey;
exportEncryptedKey input_key:inputKey key_password:secureBytes = ExportedEncryptedKey; exportEncryptedKey input_key:InputKey key_password:secureBytes = ExportedEncryptedKey;
importKey local_password:secureBytes mnemonic_password:secureBytes exported_key:exportedKey = Key; importKey local_password:secureBytes mnemonic_password:secureBytes exported_key:exportedKey = Key;
importPemKey local_password:secureBytes key_password:secureBytes exported_key:exportedPemKey = Key; importPemKey local_password:secureBytes key_password:secureBytes exported_key:exportedPemKey = Key;
importEncryptedKey local_password:secureBytes key_password:secureBytes exported_encrypted_key:exportedEncryptedKey = Key; importEncryptedKey local_password:secureBytes key_password:secureBytes exported_encrypted_key:exportedEncryptedKey = Key;
changeLocalPassword input_key:inputKey new_local_password:secureBytes = Key; changeLocalPassword input_key:InputKey new_local_password:secureBytes = Key;
encrypt decrypted_data:secureBytes secret:secureBytes = Data; encrypt decrypted_data:secureBytes secret:secureBytes = Data;
decrypt encrypted_data:secureBytes secret:secureBytes = Data; decrypt encrypted_data:secureBytes secret:secureBytes = Data;
@ -121,18 +132,20 @@ getBip39Hints prefix:string = Bip39Hints;
//raw.init initial_account_state:raw.initialAccountState = Ok; //raw.init initial_account_state:raw.initialAccountState = Ok;
raw.getAccountAddress initital_account_state:raw.initialAccountState = AccountAddress; raw.getAccountAddress initital_account_state:raw.initialAccountState = AccountAddress;
raw.getAccountState account_address:accountAddress = raw.AccountState; raw.getAccountState account_address:accountAddress = raw.AccountState;
raw.sendMessage destination:accountAddress initial_account_state:bytes data:bytes = Ok;
raw.getTransactions account_address:accountAddress from_transaction_id:internal.transactionId = raw.Transactions; raw.getTransactions account_address:accountAddress from_transaction_id:internal.transactionId = raw.Transactions;
raw.sendMessage body:bytes = Ok;
raw.createAndSendMessage destination:accountAddress initial_account_state:bytes data:bytes = Ok;
raw.createQuery destination:accountAddress init_code:bytes init_data:bytes body:bytes = query.Info;
testWallet.init private_key:inputKey = Ok; testWallet.init private_key:InputKey = Ok;
testWallet.getAccountAddress initital_account_state:testWallet.initialAccountState = AccountAddress; testWallet.getAccountAddress initital_account_state:testWallet.initialAccountState = AccountAddress;
testWallet.getAccountState account_address:accountAddress = testWallet.AccountState; testWallet.getAccountState account_address:accountAddress = testWallet.AccountState;
testWallet.sendGrams private_key:inputKey destination:accountAddress seqno:int32 amount:int64 message:bytes = SendGramsResult; testWallet.sendGrams private_key:InputKey destination:accountAddress seqno:int32 amount:int64 message:bytes = SendGramsResult;
wallet.init private_key:inputKey = Ok; wallet.init private_key:InputKey = Ok;
wallet.getAccountAddress initital_account_state:wallet.initialAccountState = AccountAddress; wallet.getAccountAddress initital_account_state:wallet.initialAccountState = AccountAddress;
wallet.getAccountState account_address:accountAddress = wallet.AccountState; wallet.getAccountState account_address:accountAddress = wallet.AccountState;
wallet.sendGrams private_key:inputKey destination:accountAddress seqno:int32 valid_until:int53 amount:int64 message:bytes = SendGramsResult; wallet.sendGrams private_key:InputKey destination:accountAddress seqno:int32 valid_until:int53 amount:int64 message:bytes = SendGramsResult;
testGiver.getAccountState = testGiver.AccountState; testGiver.getAccountState = testGiver.AccountState;
testGiver.getAccountAddress = AccountAddress; testGiver.getAccountAddress = AccountAddress;
@ -142,36 +155,50 @@ sync = Ok;
//generic.getAccountAddress initital_account_state:generic.InitialAccountState = AccountAddress; //generic.getAccountAddress initital_account_state:generic.InitialAccountState = AccountAddress;
generic.getAccountState account_address:accountAddress = generic.AccountState; generic.getAccountState account_address:accountAddress = generic.AccountState;
generic.sendGrams private_key:inputKey source:accountAddress destination:accountAddress amount:int64 timeout:int32 allow_send_to_uninited:Bool message:bytes = SendGramsResult; generic.sendGrams private_key:InputKey source:accountAddress destination:accountAddress amount:int64 timeout:int32 allow_send_to_uninited:Bool message:bytes = SendGramsResult;
generic.createSendGramsQuery private_key:InputKey source:accountAddress destination:accountAddress amount:int64 timeout:int32 allow_send_to_uninited:Bool message:bytes = query.Info;
query.send id:int53 = Ok;
query.forget id:int53 = Ok;
query.estimateFees id:int53 ignore_chksig:Bool = query.Fees;
query.getInfo id:int53 = query.Info;
smc.load account_address:accountAddress = smc.Info;
smc.getCode id:int53 = tvm.Cell;
smc.getData id:int53 = tvm.Cell;
smc.getState id:int53 = tvm.Cell;
onLiteServerQueryResult id:int64 bytes:bytes = Ok; onLiteServerQueryResult id:int64 bytes:bytes = Ok;
onLiteServerQueryError id:int64 error:error = Ok; onLiteServerQueryError id:int64 error:error = Ok;
runTests dir:string = Ok; runTests dir:string = Ok;
//@description Sets new log stream for internal logging of TDLib. This is an offline method. Can be called before authorization. Can be called synchronously @log_stream New log stream liteServer.getInfo = liteServer.Info;
//@description Sets new log stream for internal logging of tonlib. This is an offline method. Can be called before authorization. Can be called synchronously @log_stream New log stream
setLogStream log_stream:LogStream = Ok; setLogStream log_stream:LogStream = Ok;
//@description Returns information about currently used log stream for internal logging of TDLib. This is an offline method. Can be called before authorization. Can be called synchronously //@description Returns information about currently used log stream for internal logging of tonlib. This is an offline method. Can be called before authorization. Can be called synchronously
getLogStream = LogStream; getLogStream = LogStream;
//@description Sets the verbosity level of the internal logging of TDLib. This is an offline method. Can be called before authorization. Can be called synchronously //@description Sets the verbosity level of the internal logging of tonlib. This is an offline method. Can be called before authorization. Can be called synchronously
//@new_verbosity_level New value of the verbosity level for logging. Value 0 corresponds to fatal errors, value 1 corresponds to errors, value 2 corresponds to warnings and debug warnings, value 3 corresponds to informational, value 4 corresponds to debug, value 5 corresponds to verbose debug, value greater than 5 and up to 1023 can be used to enable even more logging //@new_verbosity_level New value of the verbosity level for logging. Value 0 corresponds to fatal errors, value 1 corresponds to errors, value 2 corresponds to warnings and debug warnings, value 3 corresponds to informational, value 4 corresponds to debug, value 5 corresponds to verbose debug, value greater than 5 and up to 1023 can be used to enable even more logging
setLogVerbosityLevel new_verbosity_level:int32 = Ok; setLogVerbosityLevel new_verbosity_level:int32 = Ok;
//@description Returns current verbosity level of the internal logging of TDLib. This is an offline method. Can be called before authorization. Can be called synchronously //@description Returns current verbosity level of the internal logging of tonlib. This is an offline method. Can be called before authorization. Can be called synchronously
getLogVerbosityLevel = LogVerbosityLevel; getLogVerbosityLevel = LogVerbosityLevel;
//@description Returns list of available TDLib internal log tags, for example, ["actor", "binlog", "connections", "notifications", "proxy"]. This is an offline method. Can be called before authorization. Can be called synchronously //@description Returns list of available tonlib internal log tags, for example, ["actor", "binlog", "connections", "notifications", "proxy"]. This is an offline method. Can be called before authorization. Can be called synchronously
getLogTags = LogTags; getLogTags = LogTags;
//@description Sets the verbosity level for a specified TDLib internal log tag. This is an offline method. Can be called before authorization. Can be called synchronously //@description Sets the verbosity level for a specified tonlib internal log tag. This is an offline method. Can be called before authorization. Can be called synchronously
//@tag Logging tag to change verbosity level @new_verbosity_level New verbosity level; 1-1024 //@tag Logging tag to change verbosity level @new_verbosity_level New verbosity level; 1-1024
setLogTagVerbosityLevel tag:string new_verbosity_level:int32 = Ok; setLogTagVerbosityLevel tag:string new_verbosity_level:int32 = Ok;
//@description Returns current verbosity level for a specified TDLib internal log tag. This is an offline method. Can be called before authorization. Can be called synchronously @tag Logging tag to change verbosity level //@description Returns current verbosity level for a specified tonlib internal log tag. This is an offline method. Can be called before authorization. Can be called synchronously @tag Logging tag to change verbosity level
getLogTagVerbosityLevel tag:string = LogVerbosityLevel; getLogTagVerbosityLevel tag:string = LogVerbosityLevel;
//@description Adds a message to TDLib internal log. This is an offline method. Can be called before authorization. Can be called synchronously //@description Adds a message to tonlib internal log. This is an offline method. Can be called before authorization. Can be called synchronously
//@verbosity_level Minimum verbosity level needed for the message to be logged, 0-1023 @text Text of a message to log //@verbosity_level Minimum verbosity level needed for the message to be logged, 0-1023 @text Text of a message to log
addLogMessage verbosity_level:int32 text:string = Ok; addLogMessage verbosity_level:int32 text:string = Ok;

Binary file not shown.

View file

@ -5,42 +5,34 @@ if (NOT OPENSSL_FOUND)
endif() endif()
set(TONLIB_SOURCE set(TONLIB_SOURCE
tonlib/CellString.cpp
tonlib/Client.cpp tonlib/Client.cpp
tonlib/Config.cpp tonlib/Config.cpp
tonlib/ExtClient.cpp tonlib/ExtClient.cpp
tonlib/ExtClientLazy.cpp tonlib/ExtClientLazy.cpp
tonlib/ExtClientOutbound.cpp tonlib/ExtClientOutbound.cpp
tonlib/GenericAccount.cpp
tonlib/KeyStorage.cpp tonlib/KeyStorage.cpp
tonlib/KeyValue.cpp tonlib/KeyValue.cpp
tonlib/LastBlock.cpp tonlib/LastBlock.cpp
tonlib/LastBlockStorage.cpp tonlib/LastBlockStorage.cpp
tonlib/LastConfig.cpp
tonlib/Logging.cpp tonlib/Logging.cpp
tonlib/TestGiver.cpp
tonlib/TestWallet.cpp
tonlib/TonlibClient.cpp tonlib/TonlibClient.cpp
tonlib/utils.cpp tonlib/utils.cpp
tonlib/Wallet.cpp
tonlib/CellString.h
tonlib/Client.h tonlib/Client.h
tonlib/Config.h tonlib/Config.h
tonlib/ExtClient.h tonlib/ExtClient.h
tonlib/ExtClientLazy.h tonlib/ExtClientLazy.h
tonlib/ExtClientOutbound.h tonlib/ExtClientOutbound.h
tonlib/GenericAccount.h
tonlib/KeyStorage.h tonlib/KeyStorage.h
tonlib/KeyValue.h tonlib/KeyValue.h
tonlib/LastBlock.h tonlib/LastBlock.h
tonlib/LastBlockStorage.h tonlib/LastBlockStorage.h
tonlib/LastConfig.h
tonlib/Logging.h tonlib/Logging.h
tonlib/TestGiver.h
tonlib/TestWallet.h
tonlib/TonlibCallback.h tonlib/TonlibCallback.h
tonlib/TonlibClient.h tonlib/TonlibClient.h
tonlib/utils.h tonlib/utils.h
tonlib/Wallet.h
tonlib/keys/bip39.cpp tonlib/keys/bip39.cpp
tonlib/keys/DecryptedKey.cpp tonlib/keys/DecryptedKey.cpp
@ -64,7 +56,7 @@ target_include_directories(tonlib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/.. $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/..
$<BUILD_INTERFACE:${OPENSSL_INCLUDE_DIR}> $<BUILD_INTERFACE:${OPENSSL_INCLUDE_DIR}>
) )
target_link_libraries(tonlib PRIVATE tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ton_block lite-client-common) target_link_libraries(tonlib PRIVATE tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ton_block lite-client-common smc-envelope)
target_link_libraries(tonlib PUBLIC tdutils tl_tonlib_api) target_link_libraries(tonlib PUBLIC tdutils tl_tonlib_api)
if (TONLIB_ENABLE_JNI AND NOT ANDROID) # jni is available by default on Android if (TONLIB_ENABLE_JNI AND NOT ANDROID) # jni is available by default on Android
@ -134,7 +126,7 @@ if (NOT TON_USE_ABSEIL)
if (WIN32) if (WIN32)
set(WINGETOPT_TARGET wingetopt) set(WINGETOPT_TARGET wingetopt)
endif() endif()
install(TARGETS tdnet keys crc32c tdactor adnllite tl_api tl-utils tl_lite_api tl-lite-utils ton_crypto ton_block ${WINGETOPT_TARGET} install(TARGETS tdnet keys crc32c tdactor adnllite tl_api tl-utils tl_lite_api tl-lite-utils ton_crypto ton_block smc-envelope ${WINGETOPT_TARGET}
tdutils tl_tonlib_api tonlib lite-client-common Tonlib EXPORT Tonlib tdutils tl_tonlib_api tonlib lite-client-common Tonlib EXPORT Tonlib
LIBRARY DESTINATION lib LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib ARCHIVE DESTINATION lib

View file

@ -17,23 +17,14 @@
Copyright 2017-2019 Telegram Systems LLP 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.h"
#include "block/block-auto.h" #include "block/block-auto.h"
#include "vm/cells.h" #include "vm/cells.h"
#include "vm/boc.h" #include "vm/boc.h"
#include "vm/cells/MerkleProof.h" #include "vm/cells/CellString.h"
#include "tonlib/CellString.h"
#include "tonlib/utils.h" #include "tonlib/utils.h"
#include "tonlib/TestGiver.h"
#include "tonlib/TestWallet.h"
#include "tonlib/Wallet.h"
#include "tonlib/GenericAccount.h"
#include "tonlib/TonlibClient.h" #include "tonlib/TonlibClient.h"
#include "tonlib/Client.h" #include "tonlib/Client.h"
@ -70,181 +61,6 @@ TEST(Tonlib, CellString) {
using namespace tonlib; 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 // return if recv_internal
DUP 85143 INT EQUAL IFJMP:<{ // "seqno" get-method
DROP c4 PUSHCTR CTOS 32 PLDU // cnt
}>
INC 32 THROWIF // 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:<{
// 3 INT 35 LSHIFT# 3 INT RAWRESERVE // reserve all but 103 Grams from the balance
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();
}
td::Ref<vm::Cell> get_wallet_source() {
std::string code = R"ABCD(
SETCP0 DUP IFNOTRET // return if recv_internal
DUP 85143 INT EQUAL IFJMP:<{ // "seqno" get-method
DROP c4 PUSHCTR CTOS 32 PLDU // cnt
}>
INC 32 THROWIF // fail unless recv_external
9 PUSHPOW2 LDSLICEX DUP 32 LDU 32 LDU // signature in_msg msg_seqno valid_until cs
SWAP NOW LEQ 35 THROWIF // signature in_msg msg_seqno cs
c4 PUSH CTOS 32 LDU 256 LDU ENDS // signature in_msg msg_seqno cs stored_seqno public_key
s3 s1 XCPU // signature in_msg public_key cs stored_seqno msg_seqno stored_seqno
EQUAL 33 THROWIFNOT // signature in_msg public_key cs stored_seqno
s0 s3 XCHG HASHSU // signature stored_seqno public_key cs hash
s0 s4 s2 XC2PU CHKSIGNU 34 THROWIFNOT // cs stored_seqno public_key
ACCEPT
s0 s2 XCHG // public_key stored_seqno cs
WHILE:<{
DUP SREFS // public_key stored_seqno cs _40
}>DO<{ // public_key stored_seqno cs
// 3 INT 35 LSHIFT# 3 INT RAWRESERVE // reserve all but 103 Grams from the balance
8 LDU LDREF s0 s2 XCHG // public_key stored_seqno cs _45 mode
SENDRAWMSG // public_key stored_seqno cs
}>
ENDS INC // public_key seqno'
NEWC 32 STU 256 STU ENDC c4 POP
)ABCD";
return fift::compile_asm(code).move_as_ok();
}
TEST(Tonlib, TestWallet) {
LOG(ERROR) << td::base64_encode(std_boc_serialize(get_test_wallet_source()).move_as_ok());
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());
fift_output.source_lookup.write_file("/main.fif", load_source("smartcont/wallet.fif")).ensure();
auto dest = block::StdAddress::parse("Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX").move_as_ok();
fift_output = fift::mem_run_fift(std::move(fift_output.source_lookup),
{"aba", "new-wallet", "Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "123",
"321", "-C", "TEST"})
.move_as_ok();
auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data;
auto gift_message = GenericAccount::create_ext_message(
address, {}, TestWallet::make_a_gift_message(priv_key, 123, 321000000000ll, "TEST", dest));
LOG(ERROR) << "-------";
vm::load_cell_slice(gift_message).print_rec(std::cerr);
LOG(ERROR) << "-------";
vm::load_cell_slice(vm::std_boc_deserialize(wallet_query).move_as_ok()).print_rec(std::cerr);
CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == gift_message->get_hash());
}
td::Ref<vm::Cell> get_wallet_source_fc() {
return fift::compile_asm(load_source("smartcont/wallet-code.fif"), "", false).move_as_ok();
}
TEST(Tonlib, Wallet) {
LOG(ERROR) << td::base64_encode(std_boc_serialize(get_wallet_source()).move_as_ok());
CHECK(get_wallet_source()->get_hash() == Wallet::get_init_code()->get_hash());
auto fift_output = fift::mem_run_fift(load_source("smartcont/new-wallet-v2.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 = Wallet::get_init_state(pub_key);
auto init_message = Wallet::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());
fift_output.source_lookup.write_file("/main.fif", load_source("smartcont/wallet-v2.fif")).ensure();
class ZeroOsTime : public fift::OsTime {
public:
td::uint32 now() override {
return 0;
}
};
fift_output.source_lookup.set_os_time(std::make_unique<ZeroOsTime>());
auto dest = block::StdAddress::parse("Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX").move_as_ok();
fift_output =
fift::mem_run_fift(std::move(fift_output.source_lookup),
{"aba", "new-wallet", "Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "123", "321"})
.move_as_ok();
auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data;
auto gift_message = GenericAccount::create_ext_message(
address, {}, Wallet::make_a_gift_message(priv_key, 123, 60, 321000000000ll, "TESTv2", dest));
LOG(ERROR) << "-------";
vm::load_cell_slice(gift_message).print_rec(std::cerr);
LOG(ERROR) << "-------";
vm::load_cell_slice(vm::std_boc_deserialize(wallet_query).move_as_ok()).print_rec(std::cerr);
CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == gift_message->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, "GIFT", 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, PublicKey) { TEST(Tonlib, PublicKey) {
block::PublicKey::parse("pubjns2gp7DGCnEH7EOWeCnb6Lw1akm538YYaz6sdLVHfRB2").ensure_error(); block::PublicKey::parse("pubjns2gp7DGCnEH7EOWeCnb6Lw1akm538YYaz6sdLVHfRB2").ensure_error();
auto key = block::PublicKey::parse("Pubjns2gp7DGCnEH7EOWeCnb6Lw1akm538YYaz6sdLVHfRB2").move_as_ok(); auto key = block::PublicKey::parse("Pubjns2gp7DGCnEH7EOWeCnb6Lw1akm538YYaz6sdLVHfRB2").move_as_ok();
@ -480,15 +296,15 @@ TEST(Tonlib, KeysApi) {
td::SecureString{})) td::SecureString{}))
.move_as_ok(); .move_as_ok();
sync_send(client, make_object<tonlib_api::exportKey>(make_object<tonlib_api::inputKey>( sync_send(client, make_object<tonlib_api::exportKey>(make_object<tonlib_api::inputKeyRegular>(
make_object<tonlib_api::key>(key->public_key_, key->secret_.copy()), make_object<tonlib_api::key>(key->public_key_, key->secret_.copy()),
td::SecureString("wrong password")))) td::SecureString("wrong password"))))
.ensure_error(); .ensure_error();
//exportKey input_key:inputKey = ExportedKey; //exportKey input_key:inputKeyRegular = ExportedKey;
auto exported_key = auto exported_key =
sync_send(client, sync_send(client,
make_object<tonlib_api::exportKey>(make_object<tonlib_api::inputKey>( make_object<tonlib_api::exportKey>(make_object<tonlib_api::inputKeyRegular>(
make_object<tonlib_api::key>(key->public_key_, key->secret_.copy()), local_password.copy()))) make_object<tonlib_api::key>(key->public_key_, key->secret_.copy()), local_password.copy())))
.move_as_ok(); .move_as_ok();
LOG(ERROR) << to_string(exported_key); LOG(ERROR) << to_string(exported_key);
@ -500,20 +316,20 @@ TEST(Tonlib, KeysApi) {
return word_list_copy; return word_list_copy;
}; };
//changeLocalPassword input_key:inputKey new_local_password:bytes = Key; //changeLocalPassword input_key:inputKeyRegular new_local_password:bytes = Key;
auto new_key = auto new_key =
sync_send(client, sync_send(client,
make_object<tonlib_api::changeLocalPassword>( make_object<tonlib_api::changeLocalPassword>(
make_object<tonlib_api::inputKey>( make_object<tonlib_api::inputKeyRegular>(
make_object<tonlib_api::key>(key->public_key_, key->secret_.copy()), local_password.copy()), make_object<tonlib_api::key>(key->public_key_, key->secret_.copy()), local_password.copy()),
td::SecureString("tmp local password"))) td::SecureString("tmp local password")))
.move_as_ok(); .move_as_ok();
sync_send(client, sync_send(client,
make_object<tonlib_api::exportKey>(make_object<tonlib_api::inputKey>( make_object<tonlib_api::exportKey>(make_object<tonlib_api::inputKeyRegular>(
make_object<tonlib_api::key>(key->public_key_, new_key->secret_.copy()), local_password.copy()))) make_object<tonlib_api::key>(key->public_key_, new_key->secret_.copy()), local_password.copy())))
.ensure_error(); .ensure_error();
auto exported_key2 = sync_send(client, make_object<tonlib_api::exportKey>(make_object<tonlib_api::inputKey>( auto exported_key2 = sync_send(client, make_object<tonlib_api::exportKey>(make_object<tonlib_api::inputKeyRegular>(
make_object<tonlib_api::key>(key->public_key_, new_key->secret_.copy()), make_object<tonlib_api::key>(key->public_key_, new_key->secret_.copy()),
td::SecureString("tmp local password")))) td::SecureString("tmp local password"))))
.move_as_ok(); .move_as_ok();
@ -531,7 +347,7 @@ TEST(Tonlib, KeysApi) {
auto wrong_export_password = td::SecureString("wrong_export password"); auto wrong_export_password = td::SecureString("wrong_export password");
auto exported_encrypted_key = auto exported_encrypted_key =
sync_send(client, make_object<tonlib_api::exportEncryptedKey>( sync_send(client, make_object<tonlib_api::exportEncryptedKey>(
make_object<tonlib_api::inputKey>( make_object<tonlib_api::inputKeyRegular>(
make_object<tonlib_api::key>(key->public_key_, new_key->secret_.copy()), make_object<tonlib_api::key>(key->public_key_, new_key->secret_.copy()),
td::SecureString("tmp local password")), td::SecureString("tmp local password")),
export_password.copy())) export_password.copy()))
@ -576,12 +392,12 @@ TEST(Tonlib, KeysApi) {
CHECK(imported_key->public_key_ == key->public_key_); CHECK(imported_key->public_key_ == key->public_key_);
CHECK(imported_key->secret_ != key->secret_); CHECK(imported_key->secret_ != key->secret_);
//exportPemKey input_key:inputKey key_password:bytes = ExportedPemKey; //exportPemKey input_key:inputKeyRegular key_password:bytes = ExportedPemKey;
auto pem_password = td::SecureString("pem password"); auto pem_password = td::SecureString("pem password");
auto r_exported_pem_key = sync_send( auto r_exported_pem_key = sync_send(
client, client,
make_object<tonlib_api::exportPemKey>( make_object<tonlib_api::exportPemKey>(
make_object<tonlib_api::inputKey>( make_object<tonlib_api::inputKeyRegular>(
make_object<tonlib_api::key>(key->public_key_, imported_key->secret_.copy()), new_local_password.copy()), make_object<tonlib_api::key>(key->public_key_, imported_key->secret_.copy()), new_local_password.copy()),
pem_password.copy())); pem_password.copy()));
if (r_exported_pem_key.is_error() && r_exported_pem_key.error().message() == "INTERNAL Not supported") { if (r_exported_pem_key.is_error() && r_exported_pem_key.error().message() == "INTERNAL Not supported") {

View file

@ -34,10 +34,12 @@
#include "ton/ton-tl.hpp" #include "ton/ton-tl.hpp"
#include "block/block.h" #include "block/block.h"
#include "block/block-auto.h" #include "block/block-auto.h"
#include "Ed25519.h"
#include "tonlib/GenericAccount.h" #include "smc-envelope/GenericAccount.h"
#include "tonlib/TestGiver.h" #include "smc-envelope/MultisigWallet.h"
#include "tonlib/TestWallet.h" #include "smc-envelope/TestGiver.h"
#include "smc-envelope/TestWallet.h"
#include "tonlib/LastBlock.h" #include "tonlib/LastBlock.h"
#include "tonlib/ExtClient.h" #include "tonlib/ExtClient.h"
#include "tonlib/utils.h" #include "tonlib/utils.h"
@ -57,18 +59,21 @@
#include "td/utils/optional.h" #include "td/utils/optional.h"
#include "td/utils/overloaded.h" #include "td/utils/overloaded.h"
#include "td/utils/MpscPollableQueue.h" #include "td/utils/MpscPollableQueue.h"
#include "td/utils/port/path.h"
#include "td/utils/port/signals.h" #include "td/utils/port/signals.h"
using namespace tonlib; using namespace tonlib;
constexpr td::int64 Gramm = 1000000000;
auto sync_send = [](auto& client, auto query) { auto sync_send = [](auto& client, auto query) {
using ReturnTypePtr = typename std::decay_t<decltype(*query)>::ReturnType; using ReturnTypePtr = typename std::decay_t<decltype(*query)>::ReturnType;
using ReturnType = typename ReturnTypePtr::element_type; using ReturnType = typename ReturnTypePtr::element_type;
client.send({1, std::move(query)}); client.send({1, std::move(query)});
while (true) { while (true) {
auto response = client.receive(100); auto response = client.receive(100);
if (response.object) { if (response.object && response.id != 0) {
CHECK(response.id == 1); CHECK(response.id == 1);
if (response.object->get_id() == tonlib_api::error::ID) { if (response.object->get_id() == tonlib_api::error::ID) {
auto error = tonlib_api::move_object_as<tonlib_api::error>(response.object); auto error = tonlib_api::move_object_as<tonlib_api::error>(response.object);
@ -78,87 +83,295 @@ auto sync_send = [](auto& client, auto query) {
} }
} }
}; };
auto static_send = [](auto query) {
using ReturnTypePtr = typename std::decay_t<decltype(*query)>::ReturnType;
using ReturnType = typename ReturnTypePtr::element_type;
auto response = Client::execute({1, std::move(query)});
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 { struct Key {
std::string public_key; std::string public_key;
td::SecureString secret; td::SecureString secret;
tonlib_api::object_ptr<tonlib_api::inputKey> get_input_key() const { tonlib_api::object_ptr<tonlib_api::InputKey> get_input_key() const {
return tonlib_api::make_object<tonlib_api::inputKey>( return tonlib_api::make_object<tonlib_api::inputKeyRegular>(
tonlib_api::make_object<tonlib_api::key>(public_key, secret.copy()), td::SecureString("local")); tonlib_api::make_object<tonlib_api::key>(public_key, secret.copy()), td::SecureString("local"));
} }
tonlib_api::object_ptr<tonlib_api::InputKey> get_fake_input_key() const {
return tonlib_api::make_object<tonlib_api::inputKeyFake>();
}
}; };
struct Wallet { struct Wallet {
std::string address; std::string address;
Key key; Key key;
}; };
struct TransactionId {
td::int64 lt{0};
std::string hash;
};
struct AccountState {
enum Type { Empty, Wallet, Unknown } type{Empty};
td::int64 sync_utime{-1};
td::int64 balance{-1};
TransactionId last_transaction_id;
std::string address;
bool is_inited() const {
return type != Empty;
}
};
using tonlib_api::make_object;
void sync(Client& client) {
sync_send(client, make_object<tonlib_api::sync>()).ensure();
}
std::string wallet_address(Client& client, const Key& key) {
return sync_send(client, make_object<tonlib_api::wallet_getAccountAddress>(
make_object<tonlib_api::wallet_initialAccountState>(key.public_key)))
.move_as_ok()
->account_address_;
}
Wallet import_wallet_from_pkey(Client& client, std::string pkey, std::string password) {
auto key = sync_send(client, make_object<tonlib_api::importPemKey>(
td::SecureString("local"), td::SecureString(password),
make_object<tonlib_api::exportedPemKey>(td::SecureString(pkey))))
.move_as_ok();
Wallet wallet{"", {key->public_key_, std::move(key->secret_)}};
wallet.address = wallet_address(client, wallet.key);
return wallet;
}
std::string test_giver_address(Client& client) { std::string test_giver_address(Client& client) {
using tonlib_api::make_object; using tonlib_api::make_object;
return sync_send(client, make_object<tonlib_api::testGiver_getAccountAddress>()).move_as_ok()->account_address_; return sync_send(client, make_object<tonlib_api::testGiver_getAccountAddress>()).move_as_ok()->account_address_;
} }
td::int64 get_balance(Client& client, std::string address) { AccountState get_account_state(Client& client, std::string address) {
auto generic_state = sync_send(client, tonlib_api::make_object<tonlib_api::generic_getAccountState>( auto generic_state = sync_send(client, tonlib_api::make_object<tonlib_api::generic_getAccountState>(
tonlib_api::make_object<tonlib_api::accountAddress>(address))) tonlib_api::make_object<tonlib_api::accountAddress>(address)))
.move_as_ok(); .move_as_ok();
td::int64 res = 0; AccountState res;
tonlib_api::downcast_call(*generic_state, [&](auto& state) { res = state.account_state_->balance_; }); tonlib_api::downcast_call(*generic_state, [&](auto& state) {
res.balance = state.account_state_->balance_;
res.sync_utime = state.account_state_->sync_utime_;
res.last_transaction_id.lt = state.account_state_->last_transaction_id_->lt_;
res.last_transaction_id.hash = state.account_state_->last_transaction_id_->hash_;
});
res.address = address;
switch (generic_state->get_id()) {
case tonlib_api::generic_accountStateUninited::ID:
res.type = AccountState::Empty;
break;
case tonlib_api::generic_accountStateWallet::ID:
res.type = AccountState::Wallet;
break;
default:
res.type = AccountState::Unknown;
break;
}
return res; return res;
} }
bool is_inited(Client& client, std::string address) { struct QueryId {
auto generic_state = sync_send(client, tonlib_api::make_object<tonlib_api::generic_getAccountState>( td::int64 id;
tonlib_api::make_object<tonlib_api::accountAddress>(address))) };
.move_as_ok();
return generic_state->get_id() != tonlib_api::generic_accountStateUninited::ID; struct Fee {
td::int64 in_fwd_fee{0};
td::int64 storage_fee{0};
td::int64 gas_fee{0};
td::int64 fwd_fee{0};
td::int64 sum() const {
return in_fwd_fee + storage_fee + gas_fee + fwd_fee;
}
};
template <class T>
auto to_fee(const T& fee) {
Fee res;
res.in_fwd_fee = fee->in_fwd_fee_;
res.storage_fee = fee->storage_fee_;
res.gas_fee = fee->gas_fee_;
res.fwd_fee = fee->fwd_fee_;
return res;
} }
void transfer_grams(Client& client, std::string from, std::string to, td::int64 amount, td::StringBuilder& operator<<(td::StringBuilder& sb, const Fee& fees) {
tonlib_api::object_ptr<tonlib_api::inputKey> input_key) { return sb << td::tag("in_fwd_fee", fees.in_fwd_fee) << td::tag("storage_fee", fees.storage_fee)
auto balance = get_balance(client, to); << td::tag("gas_fee", fees.gas_fee) << td::tag("fwd_fee", fees.fwd_fee);
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, 0, true, "GIFT")) struct QueryInfo {
.ensure(); td::int64 valid_until;
while (balance == get_balance(client, to)) { std::string body_hash;
};
td::Result<QueryId> create_send_grams_query(Client& client, const Wallet& source, std::string destination,
td::int64 amount, std::string message, bool force = false, int timeout = 0,
bool fake = false) {
auto r_id = sync_send(client, tonlib_api::make_object<tonlib_api::generic_createSendGramsQuery>(
fake ? source.key.get_fake_input_key() : source.key.get_input_key(),
tonlib_api::make_object<tonlib_api::accountAddress>(source.address),
tonlib_api::make_object<tonlib_api::accountAddress>(destination), amount, timeout,
force, std::move(message)));
TRY_RESULT(id, std::move(r_id));
return QueryId{id->id_};
}
td::Result<QueryId> create_raw_query(Client& client, std::string source, std::string init_code, std::string init_data,
std::string body) {
auto r_id =
sync_send(client, tonlib_api::make_object<tonlib_api::raw_createQuery>(
tonlib_api::make_object<tonlib_api::accountAddress>(source), init_code, init_data, body));
TRY_RESULT(id, std::move(r_id));
return QueryId{id->id_};
}
std::pair<Fee, Fee> query_estimate_fees(Client& client, QueryId query_id, bool ignore_chksig = false) {
auto fees = sync_send(client, tonlib_api::make_object<tonlib_api::query_estimateFees>(query_id.id, ignore_chksig))
.move_as_ok();
return std::make_pair(to_fee(fees->source_fees_), to_fee(fees->destination_fees_));
}
void query_send(Client& client, QueryId query_id) {
sync_send(client, tonlib_api::make_object<tonlib_api::query_send>(query_id.id)).ensure();
}
QueryInfo query_get_info(Client& client, QueryId query_id) {
auto info = sync_send(client, tonlib_api::make_object<tonlib_api::query_getInfo>(query_id.id)).move_as_ok();
return QueryInfo{info->valid_until_, info->body_hash_};
}
td::Result<AccountState> wait_state_change(Client& client, const AccountState& old_state, td::int64 valid_until) {
while (true) {
auto new_state = get_account_state(client, old_state.address);
if (new_state.last_transaction_id.lt != old_state.last_transaction_id.lt) {
return new_state;
}
if (valid_until != 0 && new_state.sync_utime >= valid_until) {
return td::Status::Error("valid_until expired");
}
client.receive(1); client.receive(1);
} }
};
td::Result<tonlib_api::object_ptr<tonlib_api::raw_transactions>> get_transactions(Client& client, std::string address,
const TransactionId& from) {
auto got_transactions = sync_send(client, make_object<tonlib_api::raw_getTransactions>(
make_object<tonlib_api::accountAddress>(address),
make_object<tonlib_api::internal_transactionId>(from.lt, from.hash)))
.move_as_ok();
return std::move(got_transactions);
} }
td::Status transfer_grams(Client& client, const Wallet& wallet, std::string address, td::int64 amount) {
auto src_state = get_account_state(client, wallet.address);
auto dst_state = get_account_state(client, address);
auto message = td::rand_string('a', 'z', 500);
LOG(INFO) << "Transfer: create query " << (double)amount / Gramm << " from " << wallet.address << " to " << address;
auto r_query_id = create_send_grams_query(client, wallet, address, amount, message);
if (r_query_id.is_error() && td::begins_with(r_query_id.error().message(), "DANGEROUS_TRANSACTION")) {
ASSERT_TRUE(dst_state.type == AccountState::Empty);
LOG(INFO) << "Transfer: recreate query due to DANGEROUS_TRANSACTION error";
r_query_id = create_send_grams_query(client, wallet, address, amount, message, true);
}
r_query_id.ensure();
QueryId query_id = r_query_id.move_as_ok();
auto query_info = query_get_info(client, query_id);
auto fees = query_estimate_fees(client, query_id);
LOG(INFO) << "Expected src fees: " << fees.first;
LOG(INFO) << "Expected dst fees: " << fees.second;
bool transfer_all = amount == src_state.balance;
if (!transfer_all && amount + fees.first.sum() + 10 > src_state.balance) {
return td::Status::Error("Not enough balance for query");
}
LOG(INFO) << "Transfer: send query";
query_send(client, query_id);
td::Timer timer;
TRY_RESULT(new_src_state, wait_state_change(client, src_state, query_info.valid_until));
LOG(INFO) << "Transfer: reached source in " << timer;
td::int64 lt;
td::int64 first_fee;
{
auto tr = get_transactions(client, src_state.address, new_src_state.last_transaction_id).move_as_ok();
CHECK(tr->transactions_.size() > 0);
const auto& txn = tr->transactions_[0];
CHECK(txn->in_msg_->body_hash_ == query_info.body_hash);
ASSERT_EQ(1u, txn->out_msgs_.size());
ASSERT_EQ(message, txn->out_msgs_[0]->message_);
lt = txn->out_msgs_[0]->created_lt_;
auto fee_difference = fees.first.sum() - txn->fee_;
first_fee = txn->fee_;
auto desc = PSTRING() << fee_difference << " storage:[" << fees.first.storage_fee << " vs " << txn->storage_fee_
<< "] other:[" << fees.first.sum() - fees.first.storage_fee << " vs " << txn->other_fee_
<< "]";
LOG(INFO) << "Source fee difference " << desc;
LOG_IF(ERROR, std::abs(fee_difference) > 1) << "Too big source fee difference " << desc;
}
TRY_RESULT(new_dst_state, wait_state_change(client, dst_state, new_src_state.sync_utime + 30));
LOG(INFO) << "Transfer: reached destination in " << timer;
{
auto tr = get_transactions(client, dst_state.address, new_dst_state.last_transaction_id).move_as_ok();
CHECK(tr->transactions_.size() > 0);
const auto& txn = tr->transactions_[0];
ASSERT_EQ(lt, txn->in_msg_->created_lt_);
if (transfer_all) {
ASSERT_EQ(amount - first_fee, txn->in_msg_->value_);
} else {
ASSERT_EQ(new_src_state.address, txn->in_msg_->source_);
}
ASSERT_EQ(new_src_state.address, txn->in_msg_->source_);
ASSERT_EQ(message, txn->in_msg_->message_);
auto fee_difference = fees.second.sum() - txn->fee_;
auto desc = PSTRING() << fee_difference << " storage:[" << fees.second.storage_fee << " vs " << txn->storage_fee_
<< "] other:[" << fees.second.sum() - fees.second.storage_fee << " vs " << txn->other_fee_
<< "]";
LOG(INFO) << "Destination fee difference " << desc;
LOG_IF(ERROR, std::abs(fee_difference) > 1) << "Too big destination fee difference " << desc;
}
return td::Status::OK();
}
Wallet create_empty_wallet(Client& client) { Wallet create_empty_wallet(Client& client) {
using tonlib_api::make_object; using tonlib_api::make_object;
auto key = sync_send(client, make_object<tonlib_api::createNewKey>(td::SecureString("local"), auto key = sync_send(client, make_object<tonlib_api::createNewKey>(td::SecureString("local"), td::SecureString(),
td::SecureString("mnemonic"), td::SecureString())) td::SecureString()))
.move_as_ok(); .move_as_ok();
Wallet wallet{"", {key->public_key_, std::move(key->secret_)}}; Wallet wallet{"", {key->public_key_, std::move(key->secret_)}};
auto account_address = auto account_address =
sync_send(client, make_object<tonlib_api::testWallet_getAccountAddress>( sync_send(client, make_object<tonlib_api::wallet_getAccountAddress>(
make_object<tonlib_api::testWallet_initialAccountState>(wallet.key.public_key))) make_object<tonlib_api::wallet_initialAccountState>(wallet.key.public_key)))
.move_as_ok(); .move_as_ok();
wallet.address = account_address->account_address_; wallet.address = account_address->account_address_;
// get state of empty account
auto state = get_account_state(client, wallet.address);
ASSERT_EQ(-1, state.balance);
ASSERT_EQ(AccountState::Empty, state.type);
return wallet; 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) { void dump_transaction_history(Client& client, std::string address) {
using tonlib_api::make_object; using tonlib_api::make_object;
auto state = sync_send(client, make_object<tonlib_api::testGiver_getAccountState>()).move_as_ok(); auto state = sync_send(client, make_object<tonlib_api::testGiver_getAccountState>()).move_as_ok();
@ -180,166 +393,156 @@ void dump_transaction_history(Client& client, std::string address) {
LOG(ERROR) << cnt; LOG(ERROR) << cnt;
} }
void test_estimate_fees_without_key(Client& client, const Wallet& wallet_a, const Wallet& wallet_b) {
LOG(ERROR) << " SUBTEST: estimate fees without key";
{
auto query_id = create_send_grams_query(client, wallet_a, wallet_b.address, 0, "???", true, 0, true).move_as_ok();
auto fees1 = query_estimate_fees(client, query_id, false);
auto fees2 = query_estimate_fees(client, query_id, true);
LOG(INFO) << "Fee without ignore_chksig\t" << fees1;
LOG(INFO) << "Fee with ignore_chksig\t" << fees2;
CHECK(fees1.first.gas_fee == 0);
CHECK(fees2.first.gas_fee != 0);
}
}
void test_back_and_forth_transfer(Client& client, const Wallet& giver_wallet, bool flag) {
LOG(ERROR) << "TEST: back and forth transfer";
// just generate private key and address
auto wallet_a = create_empty_wallet(client);
LOG(INFO) << wallet_a.address;
// get state of empty account
auto state = get_account_state(client, wallet_a.address);
ASSERT_EQ(-1, state.balance);
ASSERT_EQ(AccountState::Empty, state.type);
test_estimate_fees_without_key(client, giver_wallet, wallet_a);
// transfer from giver to a
transfer_grams(client, giver_wallet, wallet_a.address, 1 * Gramm).ensure();
state = get_account_state(client, wallet_a.address);
ASSERT_EQ(1 * Gramm, state.balance);
ASSERT_EQ(AccountState::Empty, state.type);
test_estimate_fees_without_key(client, wallet_a, giver_wallet);
if (flag) {
// transfer from a to giver
transfer_grams(client, wallet_a, giver_wallet.address, 5 * Gramm / 10).ensure();
state = get_account_state(client, wallet_a.address);
ASSERT_TRUE(state.balance < 5 * Gramm / 10);
ASSERT_EQ(AccountState::Wallet, state.type);
}
// transfer all remaining balance (test flag 128)
transfer_grams(client, wallet_a, giver_wallet.address, state.balance).ensure();
state = get_account_state(client, wallet_a.address);
ASSERT_TRUE(state.balance == 0);
ASSERT_EQ(AccountState::Wallet, state.type);
}
void test_multisig(Client& client, const Wallet& giver_wallet) {
LOG(ERROR) << "TEST: multisig";
int n = 16;
int k = 10;
std::vector<td::Ed25519::PrivateKey> private_keys;
for (int i = 0; i < n; i++) {
private_keys.push_back(td::Ed25519::generate_private_key().move_as_ok());
}
auto ms = ton::MultisigWallet::create();
auto init_data = ms->create_init_data(
td::transform(private_keys, [](const auto& pk) { return pk.get_public_key().move_as_ok().as_octet_string(); }),
k);
ms = ton::MultisigWallet::create(init_data);
auto raw_address = ms->get_address(ton::basechainId);
auto address = raw_address.rserialize();
transfer_grams(client, giver_wallet, address, 1 * Gramm).ensure();
auto init_state = ms->get_init_state();
// Just transfer all (some) money back in one query
vm::CellBuilder icb;
ton::GenericAccount::store_int_message(icb, block::StdAddress::parse(giver_wallet.address).move_as_ok(),
5 * Gramm / 10);
icb.store_bytes("\0\0\0\0", 4);
vm::CellString::store(icb, "Greatings from multisig", 35 * 8).ensure();
ton::MultisigWallet::QueryBuilder qb(-1, icb.finalize());
for (int i = 0; i < k - 1; i++) {
qb.sign(i, private_keys[i]);
}
auto query_id =
create_raw_query(client, address, vm::std_boc_serialize(ms->get_state().code).move_as_ok().as_slice().str(),
vm::std_boc_serialize(ms->get_state().data).move_as_ok().as_slice().str(),
vm::std_boc_serialize(qb.create(k - 1, private_keys[k - 1])).move_as_ok().as_slice().str())
.move_as_ok();
auto fees = query_estimate_fees(client, query_id);
LOG(INFO) << "Expected src fees: " << fees.first;
LOG(INFO) << "Expected dst fees: " << fees.second;
auto a_state = get_account_state(client, address);
query_send(client, query_id);
auto new_a_state = wait_state_change(client, a_state, a_state.sync_utime + 30).move_as_ok();
}
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
td::set_default_failure_signal_handler(); td::set_default_failure_signal_handler();
using tonlib_api::make_object; using tonlib_api::make_object;
td::OptionsParser p; td::OptionsParser p;
std::string global_config_str; std::string global_config_str;
std::string giver_key_str;
std::string giver_key_pwd = "cucumber";
std::string keystore_dir = "test-keystore";
bool reset_keystore_dir = false;
p.add_option('C', "global-config", "file to read global config", [&](td::Slice fname) { p.add_option('C', "global-config", "file to read global config", [&](td::Slice fname) {
TRY_RESULT(str, td::read_file_str(fname.str())); TRY_RESULT(str, td::read_file_str(fname.str()));
global_config_str = std::move(str); global_config_str = std::move(str);
LOG(ERROR) << global_config_str; return td::Status::OK();
});
p.add_option('G', "giver-key", "file with a wallet key that should be used as a giver", [&](td::Slice fname) {
TRY_RESULT(str, td::read_file_str(fname.str()));
giver_key_str = std::move(str);
return td::Status::OK();
});
p.add_option('f', "force", "reser keystore dir", [&]() {
reset_keystore_dir = true;
return td::Status::OK(); return td::Status::OK();
}); });
p.run(argc, argv).ensure(); p.run(argc, argv).ensure();
if (reset_keystore_dir) {
td::rmrf(keystore_dir).ignore();
td::mkdir(keystore_dir).ensure();
}
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(INFO));
static_send(make_object<tonlib_api::setLogTagVerbosityLevel>("tonlib_query", 4)).ensure();
auto tags = static_send(make_object<tonlib_api::getLogTags>()).move_as_ok()->tags_;
for (auto& tag : tags) {
static_send(make_object<tonlib_api::setLogTagVerbosityLevel>(tag, 4)).ensure();
}
Client client; Client client;
{ {
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>( 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>(".")))) make_object<tonlib_api::keyStoreTypeDirectory>(keystore_dir))))
.ensure(); .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>(
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>( // wait till client is synchronized with blockchain.
td::SecureString("local"), td::SecureString("mnemonic"), td::SecureString())) // not necessary, but synchronized will be trigged anyway later
.move_as_ok(); sync(client);
auto create_input_key = [&] { // give wallet with some test grams to run test
return make_object<tonlib_api::inputKey>(make_object<tonlib_api::key>(key->public_key_, key->secret_.copy()), auto giver_wallet = import_wallet_from_pkey(client, giver_key_str, giver_key_pwd);
td::SecureString("local"));
};
auto public_key_raw = key->public_key_; test_back_and_forth_transfer(client, giver_wallet, false);
td::Ed25519::PublicKey public_key_std(td::SecureString{public_key_raw}); test_back_and_forth_transfer(client, giver_wallet, true);
test_multisig(client, giver_wallet);
sync_send(client, make_object<tonlib_api::options_setConfig>(
make_object<tonlib_api::config>(global_config_str, "", false, false)))
.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, "GIFT"))
.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, 0,
true, "GIFT"))
.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; return 0;
} }

View file

@ -134,7 +134,7 @@ class Client::Impl final {
}; };
Client::Client() : impl_(std::make_unique<Impl>()) { Client::Client() : impl_(std::make_unique<Impl>()) {
// At least it should be enough for everybody who uses TDLib // At least it should be enough for everybody who uses tonlib
// FIXME // FIXME
//td::init_openssl_threads(); //td::init_openssl_threads();
} }

View file

@ -19,12 +19,27 @@
#include "tonlib/ExtClient.h" #include "tonlib/ExtClient.h"
#include "tonlib/LastBlock.h" #include "tonlib/LastBlock.h"
#include "tonlib/LastConfig.h"
namespace tonlib { namespace tonlib {
ExtClient::~ExtClient() { ExtClient::~ExtClient() {
last_config_queries_.for_each([](auto id, auto &promise) { promise.set_error(TonlibError::Cancelled()); });
last_block_queries_.for_each([](auto id, auto &promise) { promise.set_error(TonlibError::Cancelled()); }); last_block_queries_.for_each([](auto id, auto &promise) { promise.set_error(TonlibError::Cancelled()); });
queries_.for_each([](auto id, auto &promise) { promise.set_error(TonlibError::Cancelled()); }); queries_.for_each([](auto id, auto &promise) { promise.set_error(TonlibError::Cancelled()); });
} }
void ExtClient::with_last_config(td::Promise<LastConfigState> promise) {
auto query_id = last_config_queries_.create(std::move(promise));
td::Promise<LastConfigState> P = [query_id, self = this,
actor_id = td::actor::actor_id()](td::Result<LastConfigState> result) {
send_lambda(actor_id, [self, query_id, result = std::move(result)]() mutable {
self->last_config_queries_.extract(query_id).set_result(std::move(result));
});
};
if (client_.last_block_actor_.empty()) {
return P.set_error(TonlibError::NoLiteServers());
}
td::actor::send_closure(client_.last_config_actor_, &LastConfig::get_last_config, std::move(P));
}
void ExtClient::with_last_block(td::Promise<LastBlockState> promise) { void ExtClient::with_last_block(td::Promise<LastBlockState> promise) {
auto query_id = last_block_queries_.create(std::move(promise)); auto query_id = last_block_queries_.create(std::move(promise));
td::Promise<LastBlockState> P = [query_id, self = this, td::Promise<LastBlockState> P = [query_id, self = this,

View file

@ -33,10 +33,13 @@
namespace tonlib { namespace tonlib {
class LastBlock; class LastBlock;
class LastConfig;
struct LastBlockState; struct LastBlockState;
struct LastConfigState;
struct ExtClientRef { struct ExtClientRef {
td::actor::ActorId<ton::adnl::AdnlExtClient> andl_ext_client_; td::actor::ActorId<ton::adnl::AdnlExtClient> andl_ext_client_;
td::actor::ActorId<LastBlock> last_block_actor_; td::actor::ActorId<LastBlock> last_block_actor_;
td::actor::ActorId<LastConfig> last_config_actor_;
}; };
class ExtClient { class ExtClient {
@ -56,6 +59,7 @@ class ExtClient {
~ExtClient(); ~ExtClient();
void with_last_block(td::Promise<LastBlockState> promise); void with_last_block(td::Promise<LastBlockState> promise);
void with_last_config(td::Promise<LastConfigState> promise);
template <class QueryT> template <class QueryT>
void send_query(QueryT query, td::Promise<typename QueryT::ReturnType> promise, td::int32 seq_no = -1) { void send_query(QueryT query, td::Promise<typename QueryT::ReturnType> promise, td::int32 seq_no = -1) {
@ -94,6 +98,7 @@ class ExtClient {
ExtClientRef client_; ExtClientRef client_;
td::Container<td::Promise<td::BufferSlice>> queries_; td::Container<td::Promise<td::BufferSlice>> queries_;
td::Container<td::Promise<LastBlockState>> last_block_queries_; td::Container<td::Promise<LastBlockState>> last_block_queries_;
td::Container<td::Promise<LastConfigState>> last_config_queries_;
void send_raw_query(td::BufferSlice query, td::Promise<td::BufferSlice> promise); void send_raw_query(td::BufferSlice query, td::Promise<td::BufferSlice> promise);
}; };

View file

@ -17,6 +17,7 @@
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2019 Telegram Systems LLP
*/ */
#include "ExtClientLazy.h" #include "ExtClientLazy.h"
#include "TonlibError.h"
namespace tonlib { namespace tonlib {
class ExtClientLazyImp : public ton::adnl::AdnlExtClient { class ExtClientLazyImp : public ton::adnl::AdnlExtClient {
@ -28,12 +29,18 @@ class ExtClientLazyImp : public ton::adnl::AdnlExtClient {
void check_ready(td::Promise<td::Unit> promise) override { void check_ready(td::Promise<td::Unit> promise) override {
before_query(); before_query();
if (client_.empty()) {
return promise.set_error(TonlibError::Cancelled());
}
send_closure(client_, &ton::adnl::AdnlExtClient::check_ready, std::move(promise)); send_closure(client_, &ton::adnl::AdnlExtClient::check_ready, std::move(promise));
} }
void send_query(std::string name, td::BufferSlice data, td::Timestamp timeout, void send_query(std::string name, td::BufferSlice data, td::Timestamp timeout,
td::Promise<td::BufferSlice> promise) override { td::Promise<td::BufferSlice> promise) override {
before_query(); before_query();
if (client_.empty()) {
return promise.set_error(TonlibError::Cancelled());
}
send_closure(client_, &ton::adnl::AdnlExtClient::send_query, std::move(name), std::move(data), timeout, send_closure(client_, &ton::adnl::AdnlExtClient::send_query, std::move(name), std::move(data), timeout,
std::move(promise)); std::move(promise));
} }

View file

@ -106,6 +106,9 @@ td::Result<KeyStorage::ExportedKey> KeyStorage::export_key(InputKey input_key) {
} }
td::Result<KeyStorage::PrivateKey> KeyStorage::load_private_key(InputKey input_key) { td::Result<KeyStorage::PrivateKey> KeyStorage::load_private_key(InputKey input_key) {
if (is_fake_input_key(input_key)) {
return fake_private_key();
}
TRY_RESULT(decrypted_key, export_decrypted_key(std::move(input_key))); TRY_RESULT(decrypted_key, export_decrypted_key(std::move(input_key)));
PrivateKey private_key; PrivateKey private_key;
private_key.private_key = decrypted_key.private_key.as_octet_string(); private_key.private_key = decrypted_key.private_key.as_octet_string();
@ -166,20 +169,46 @@ td::Result<KeyStorage::Key> KeyStorage::import_pem_key(td::Slice local_password,
return save_key(DecryptedKey({}, std::move(key)), local_password); return save_key(DecryptedKey({}, std::move(key)), local_password);
} }
static std::string dummy_secret = "dummy secret of 32 bytes length!"; td::SecureString get_dummy_secret() {
return td::SecureString("dummy secret of 32 bytes length!");
}
td::Result<KeyStorage::ExportedEncryptedKey> KeyStorage::export_encrypted_key(InputKey input_key, td::Result<KeyStorage::ExportedEncryptedKey> KeyStorage::export_encrypted_key(InputKey input_key,
td::Slice key_password) { td::Slice key_password) {
TRY_RESULT(decrypted_key, export_decrypted_key(std::move(input_key))); TRY_RESULT(decrypted_key, export_decrypted_key(std::move(input_key)));
auto res = decrypted_key.encrypt(key_password, dummy_secret); auto res = decrypted_key.encrypt(key_password, get_dummy_secret());
return ExportedEncryptedKey{std::move(res.encrypted_data)}; return ExportedEncryptedKey{std::move(res.encrypted_data)};
} }
td::Result<KeyStorage::Key> KeyStorage::import_encrypted_key(td::Slice local_password, td::Slice key_password, td::Result<KeyStorage::Key> KeyStorage::import_encrypted_key(td::Slice local_password, td::Slice key_password,
ExportedEncryptedKey exported_key) { ExportedEncryptedKey exported_key) {
EncryptedKey encrypted_key{std::move(exported_key.data), td::Ed25519::PublicKey(td::SecureString()), EncryptedKey encrypted_key{std::move(exported_key.data), td::Ed25519::PublicKey(td::SecureString()),
td::SecureString(dummy_secret)}; get_dummy_secret()};
TRY_RESULT_PREFIX(decrypted_key, encrypted_key.decrypt(key_password, false), TonlibError::KeyDecrypt()); TRY_RESULT_PREFIX(decrypted_key, encrypted_key.decrypt(key_password, false), TonlibError::KeyDecrypt());
return save_key(std::move(decrypted_key), local_password); return save_key(std::move(decrypted_key), local_password);
} }
KeyStorage::PrivateKey KeyStorage::fake_private_key() {
return PrivateKey{td::SecureString(32, 0)};
}
KeyStorage::InputKey KeyStorage::fake_input_key() {
return InputKey{{td::SecureString(32, 0), td::SecureString(32, 0)}, {}};
}
bool KeyStorage::is_fake_input_key(InputKey &input_key) {
auto is_zero = [](td::Slice slice, size_t size) {
if (slice.size() != size) {
return false;
}
for (auto c : slice) {
if (c != 0) {
return false;
}
}
return true;
};
return is_zero(input_key.local_password, 0) && is_zero(input_key.key.secret, 32) &&
is_zero(input_key.key.public_key, 32);
}
} // namespace tonlib } // namespace tonlib

View file

@ -69,6 +69,10 @@ class KeyStorage {
td::Result<PrivateKey> load_private_key(InputKey input_key); td::Result<PrivateKey> load_private_key(InputKey input_key);
static PrivateKey fake_private_key();
static InputKey fake_input_key();
static bool is_fake_input_key(InputKey& input_key);
private: private:
std::shared_ptr<KeyValue> kv_; std::shared_ptr<KeyValue> kv_;

View file

@ -17,6 +17,7 @@
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2019 Telegram Systems LLP
*/ */
#include "tonlib/LastBlock.h" #include "tonlib/LastBlock.h"
#include "tonlib/LastConfig.h"
#include "tonlib/utils.h" #include "tonlib/utils.h"
@ -271,7 +272,7 @@ void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id, td::Slice s
} }
if (!state_.zero_state_id.is_valid()) { if (!state_.zero_state_id.is_valid()) {
LOG(INFO) << "Init zerostate from " << source << ": " << zero_state_id.to_str(); VLOG(last_block) << "Init zerostate from " << source << ": " << zero_state_id.to_str();
state_.zero_state_id = std::move(zero_state_id); state_.zero_state_id = std::move(zero_state_id);
return; return;
} }
@ -295,7 +296,7 @@ bool LastBlock::update_mc_last_block(ton::BlockIdExt mc_block_id) {
} }
if (!state_.last_block_id.is_valid() || state_.last_block_id.id.seqno < mc_block_id.id.seqno) { if (!state_.last_block_id.is_valid() || state_.last_block_id.id.seqno < mc_block_id.id.seqno) {
state_.last_block_id = mc_block_id; state_.last_block_id = mc_block_id;
LOG(INFO) << "Update masterchain block id: " << state_.last_block_id.to_str(); VLOG(last_block) << "Update masterchain block id: " << state_.last_block_id.to_str();
return true; return true;
} }
return false; return false;
@ -311,7 +312,7 @@ bool LastBlock::update_mc_last_key_block(ton::BlockIdExt mc_key_block_id) {
} }
if (!state_.last_key_block_id.is_valid() || state_.last_key_block_id.id.seqno < mc_key_block_id.id.seqno) { if (!state_.last_key_block_id.is_valid() || state_.last_key_block_id.id.seqno < mc_key_block_id.id.seqno) {
state_.last_key_block_id = mc_key_block_id; state_.last_key_block_id = mc_key_block_id;
LOG(INFO) << "Update masterchain key block id: " << state_.last_key_block_id.to_str(); VLOG(last_block) << "Update masterchain key block id: " << state_.last_key_block_id.to_str();
//LOG(ERROR) << td::int64(state_.last_key_block_id.id.shard) << " " //LOG(ERROR) << td::int64(state_.last_key_block_id.id.shard) << " "
//<< td::base64_encode(state_.last_key_block_id.file_hash.as_slice()) << " " //<< td::base64_encode(state_.last_key_block_id.file_hash.as_slice()) << " "
//<< td::base64_encode(state_.last_key_block_id.root_hash.as_slice()); //<< td::base64_encode(state_.last_key_block_id.root_hash.as_slice());
@ -330,7 +331,7 @@ bool LastBlock::update_init_block(ton::BlockIdExt init_block_id) {
} }
if (state_.init_block_id != init_block_id) { if (state_.init_block_id != init_block_id) {
state_.init_block_id = init_block_id; state_.init_block_id = init_block_id;
LOG(INFO) << "Update init block id: " << state_.init_block_id.to_str(); VLOG(last_block) << "Update init block id: " << state_.init_block_id.to_str();
return true; return true;
} }
return false; return false;

View file

@ -0,0 +1,152 @@
/*
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/LastConfig.h"
#include "tonlib/utils.h"
#include "ton/lite-tl.hpp"
#include "block/check-proof.h"
#include "block/mc-config.h"
#include "block/block-auto.h"
#include "lite-client/lite-client-common.h"
#include "LastBlock.h"
namespace tonlib {
// init_state <-> last_key_block
// state.valitated_init_state
// last_key_block ->
//
td::StringBuilder& operator<<(td::StringBuilder& sb, const LastConfigState& state) {
return sb;
}
LastConfig::LastConfig(ExtClientRef client, td::unique_ptr<Callback> callback) : callback_(std::move(callback)) {
client_.set_client(client);
VLOG(last_block) << "State: " << state_;
}
void LastConfig::get_last_config(td::Promise<LastConfigState> promise) {
if (promises_.empty() && get_config_state_ == QueryState::Done) {
VLOG(last_config) << "start";
VLOG(last_config) << "get_config: reset";
get_config_state_ = QueryState::Empty;
}
promises_.push_back(std::move(promise));
loop();
}
void LastConfig::with_last_block(td::Result<LastBlockState> r_last_block) {
if (r_last_block.is_error()) {
on_error(r_last_block.move_as_error());
return;
}
auto last_block = r_last_block.move_as_ok();
auto params = params_;
client_.send_query(ton::lite_api::liteServer_getConfigParams(0, create_tl_lite_block_id(last_block.last_block_id),
std::move(params)),
[this](auto r_config) { this->on_config(std::move(r_config)); });
}
void LastConfig::on_config(td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_configInfo>> r_config) {
auto status = process_config(std::move(r_config));
if (status.is_ok()) {
on_ok();
get_config_state_ = QueryState::Done;
} else {
on_error(std::move(status));
get_config_state_ = QueryState::Empty;
}
}
td::Status LastConfig::process_config(
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_configInfo>> r_config) {
TRY_RESULT(raw_config, std::move(r_config));
TRY_STATUS_PREFIX(TRY_VM(process_config_proof(std::move(raw_config))), TonlibError::ValidateConfig());
return td::Status::OK();
}
td::Status LastConfig::process_config_proof(ton::ton_api::object_ptr<ton::lite_api::liteServer_configInfo> raw_config) {
auto blkid = create_block_id(raw_config->id_);
if (!blkid.is_masterchain_ext()) {
return td::Status::Error(PSLICE() << "reference block " << blkid.to_str()
<< " for the configuration is not a valid masterchain block");
}
TRY_RESULT(state, block::check_extract_state_proof(blkid, raw_config->state_proof_.as_slice(),
raw_config->config_proof_.as_slice()));
TRY_RESULT(config, block::Config::extract_from_state(std::move(state), 0));
for (auto i : params_) {
VLOG(last_config) << "ConfigParam(" << i << ") = ";
auto value = config->get_config_param(i);
if (value.is_null()) {
VLOG(last_config) << "(null)\n";
} else {
std::ostringstream os;
if (i >= 0) {
block::gen::ConfigParam{i}.print_ref(os, value);
os << std::endl;
}
vm::load_cell_slice(value).print_rec(os);
VLOG(last_config) << os.str();
}
}
state_.config.reset(config.release());
return td::Status::OK();
}
void LastConfig::loop() {
if (promises_.empty()) {
return;
}
if (get_config_state_ == QueryState::Empty) {
VLOG(last_block) << "get_config: start";
get_config_state_ = QueryState::Active;
client_.with_last_block(
[self = this](td::Result<LastBlockState> r_last_block) { self->with_last_block(std::move(r_last_block)); });
}
}
void LastConfig::on_ok() {
VLOG(last_block) << "ok " << state_;
for (auto& promise : promises_) {
auto state = state_;
promise.set_value(std::move(state));
}
promises_.clear();
}
void LastConfig::on_error(td::Status status) {
VLOG(last_config) << "error " << status;
for (auto& promise : promises_) {
promise.set_error(status.clone());
}
promises_.clear();
}
void LastConfig::tear_down() {
on_error(TonlibError::Cancelled());
}
} // namespace tonlib

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
*/
#pragma once
#include "td/actor/actor.h"
#include "tonlib/Config.h"
#include "tonlib/ExtClient.h"
#include "td/utils/CancellationToken.h"
#include "td/utils/tl_helpers.h"
#include "block/mc-config.h"
namespace tonlib {
struct LastConfigState {
std::shared_ptr<const block::Config> config;
};
td::StringBuilder& operator<<(td::StringBuilder& sb, const LastConfigState& state);
class LastConfig : public td::actor::Actor {
public:
class Callback {
public:
virtual ~Callback() {
}
};
explicit LastConfig(ExtClientRef client, td::unique_ptr<Callback> callback);
void get_last_config(td::Promise<LastConfigState> promise);
private:
td::unique_ptr<Callback> callback_;
ExtClient client_;
LastConfigState state_;
enum class QueryState { Empty, Active, Done };
QueryState get_config_state_{QueryState::Empty};
std::vector<td::Promise<LastConfigState>> promises_;
std::vector<td::int32> params_{18, 20, 21, 24, 25};
void with_last_block(td::Result<LastBlockState> r_last_block);
void on_config(td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_configInfo>> r_config);
td::Status process_config(td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_configInfo>> r_config);
td::Status process_config_proof(ton::ton_api::object_ptr<ton::lite_api::liteServer_configInfo> config);
void on_ok();
void on_error(td::Status status);
void loop() override;
void tear_down() override;
};
} // namespace tonlib

View file

@ -32,14 +32,22 @@
namespace tonlib { namespace tonlib {
static std::mutex logging_mutex; struct LogData {
static td::FileLog file_log; std::mutex logging_mutex;
static td::TsLog ts_log(&file_log); td::FileLog file_log;
static td::NullLog null_log; td::TsLog ts_log{&file_log};
td::NullLog null_log;
};
auto &log_data() {
static LogData data;
return data;
}
#define ADD_TAG(tag) \ #define ADD_TAG(tag) \
{ #tag, &VERBOSITY_NAME(tag) } { #tag, &VERBOSITY_NAME(tag) }
static const std::map<td::Slice, int *> log_tags{ADD_TAG(tonlib_query), ADD_TAG(last_block)}; static const std::map<td::Slice, int *> log_tags{ADD_TAG(tonlib_query), ADD_TAG(last_block), ADD_TAG(last_config),
ADD_TAG(lite_server)};
#undef ADD_TAG #undef ADD_TAG
td::Status Logging::set_current_stream(tonlib_api::object_ptr<tonlib_api::LogStream> stream) { td::Status Logging::set_current_stream(tonlib_api::object_ptr<tonlib_api::LogStream> stream) {
@ -47,7 +55,7 @@ td::Status Logging::set_current_stream(tonlib_api::object_ptr<tonlib_api::LogStr
return td::Status::Error("Log stream must not be empty"); return td::Status::Error("Log stream must not be empty");
} }
std::lock_guard<std::mutex> lock(logging_mutex); std::lock_guard<std::mutex> lock(log_data().logging_mutex);
switch (stream->get_id()) { switch (stream->get_id()) {
case tonlib_api::logStreamDefault::ID: case tonlib_api::logStreamDefault::ID:
td::log_interface = td::default_log_interface; td::log_interface = td::default_log_interface;
@ -59,13 +67,13 @@ td::Status Logging::set_current_stream(tonlib_api::object_ptr<tonlib_api::LogStr
return td::Status::Error("Max log file size should be positive"); return td::Status::Error("Max log file size should be positive");
} }
TRY_STATUS(file_log.init(file_stream->path_, max_log_file_size)); TRY_STATUS(log_data().file_log.init(file_stream->path_, max_log_file_size));
std::atomic_thread_fence(std::memory_order_release); // better than nothing std::atomic_thread_fence(std::memory_order_release); // better than nothing
td::log_interface = &ts_log; td::log_interface = &log_data().ts_log;
return td::Status::OK(); return td::Status::OK();
} }
case tonlib_api::logStreamEmpty::ID: case tonlib_api::logStreamEmpty::ID:
td::log_interface = &null_log; td::log_interface = &log_data().null_log;
return td::Status::OK(); return td::Status::OK();
default: default:
UNREACHABLE(); UNREACHABLE();
@ -74,22 +82,22 @@ td::Status Logging::set_current_stream(tonlib_api::object_ptr<tonlib_api::LogStr
} }
td::Result<tonlib_api::object_ptr<tonlib_api::LogStream>> Logging::get_current_stream() { td::Result<tonlib_api::object_ptr<tonlib_api::LogStream>> Logging::get_current_stream() {
std::lock_guard<std::mutex> lock(logging_mutex); std::lock_guard<std::mutex> lock(log_data().logging_mutex);
if (td::log_interface == td::default_log_interface) { if (td::log_interface == td::default_log_interface) {
return tonlib_api::make_object<tonlib_api::logStreamDefault>(); return tonlib_api::make_object<tonlib_api::logStreamDefault>();
} }
if (td::log_interface == &null_log) { if (td::log_interface == &log_data().null_log) {
return tonlib_api::make_object<tonlib_api::logStreamEmpty>(); return tonlib_api::make_object<tonlib_api::logStreamEmpty>();
} }
if (td::log_interface == &ts_log) { if (td::log_interface == &log_data().ts_log) {
return tonlib_api::make_object<tonlib_api::logStreamFile>(file_log.get_path().str(), return tonlib_api::make_object<tonlib_api::logStreamFile>(log_data().file_log.get_path().str(),
file_log.get_rotate_threshold()); log_data().file_log.get_rotate_threshold());
} }
return td::Status::Error("Log stream is unrecognized"); return td::Status::Error("Log stream is unrecognized");
} }
td::Status Logging::set_verbosity_level(int new_verbosity_level) { td::Status Logging::set_verbosity_level(int new_verbosity_level) {
std::lock_guard<std::mutex> lock(logging_mutex); std::lock_guard<std::mutex> lock(log_data().logging_mutex);
if (0 <= new_verbosity_level && new_verbosity_level <= VERBOSITY_NAME(NEVER)) { if (0 <= new_verbosity_level && new_verbosity_level <= VERBOSITY_NAME(NEVER)) {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL) + new_verbosity_level); SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL) + new_verbosity_level);
return td::Status::OK(); return td::Status::OK();
@ -99,7 +107,7 @@ td::Status Logging::set_verbosity_level(int new_verbosity_level) {
} }
int Logging::get_verbosity_level() { int Logging::get_verbosity_level() {
std::lock_guard<std::mutex> lock(logging_mutex); std::lock_guard<std::mutex> lock(log_data().logging_mutex);
return GET_VERBOSITY_LEVEL(); return GET_VERBOSITY_LEVEL();
} }
@ -113,7 +121,7 @@ td::Status Logging::set_tag_verbosity_level(td::Slice tag, int new_verbosity_lev
return td::Status::Error("Log tag is not found"); return td::Status::Error("Log tag is not found");
} }
std::lock_guard<std::mutex> lock(logging_mutex); std::lock_guard<std::mutex> lock(log_data().logging_mutex);
*it->second = td::clamp(new_verbosity_level, 1, VERBOSITY_NAME(NEVER)); *it->second = td::clamp(new_verbosity_level, 1, VERBOSITY_NAME(NEVER));
return td::Status::OK(); return td::Status::OK();
} }
@ -124,7 +132,7 @@ td::Result<int> Logging::get_tag_verbosity_level(td::Slice tag) {
return td::Status::Error("Log tag is not found"); return td::Status::Error("Log tag is not found");
} }
std::lock_guard<std::mutex> lock(logging_mutex); std::lock_guard<std::mutex> lock(log_data().logging_mutex);
return *it->second; return *it->second;
} }

File diff suppressed because it is too large Load diff

View file

@ -34,6 +34,17 @@
#include <map> #include <map>
namespace tonlib { namespace tonlib {
namespace int_api {
struct GetAccountState;
struct GetPrivateKey;
struct SendMessage;
inline std::string to_string(const int_api::SendMessage&) {
return "Send message";
}
} // namespace int_api
class AccountState;
class Query;
class TonlibClient : public td::actor::Actor { class TonlibClient : public td::actor::Actor {
public: public:
template <class T> template <class T>
@ -66,6 +77,7 @@ class TonlibClient : public td::actor::Actor {
td::actor::ActorOwn<ton::adnl::AdnlExtClient> raw_client_; td::actor::ActorOwn<ton::adnl::AdnlExtClient> raw_client_;
td::actor::ActorId<ExtClientOutbound> ext_client_outbound_; td::actor::ActorId<ExtClientOutbound> ext_client_outbound_;
td::actor::ActorOwn<LastBlock> raw_last_block_; td::actor::ActorOwn<LastBlock> raw_last_block_;
td::actor::ActorOwn<LastConfig> raw_last_config_;
ExtClient client_; ExtClient client_;
td::CancellationTokenSource source_; td::CancellationTokenSource source_;
@ -76,6 +88,7 @@ class TonlibClient : public td::actor::Actor {
ExtClientRef get_client_ref(); ExtClientRef get_client_ref();
void init_ext_client(); void init_ext_client();
void init_last_block(); void init_last_block();
void init_last_config();
bool is_closing_{false}; bool is_closing_{false};
td::uint32 ref_cnt_{1}; td::uint32 ref_cnt_{1};
@ -127,9 +140,53 @@ class TonlibClient : public td::actor::Actor {
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::decrypt& request); static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::decrypt& request);
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::kdf& request); static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::kdf& request);
template <class P>
td::Status do_request(const tonlib_api::runTests& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::raw_getAccountAddress& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::testWallet_getAccountAddress& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::wallet_getAccountAddress& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::testGiver_getAccountAddress& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::packAccountAddress& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::unpackAccountAddress& request, P&&);
template <class P>
td::Status do_request(tonlib_api::getBip39Hints& request, P&&);
template <class P>
td::Status do_request(tonlib_api::setLogStream& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::getLogStream& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::setLogVerbosityLevel& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::setLogTagVerbosityLevel& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::getLogVerbosityLevel& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::getLogTagVerbosityLevel& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::getLogTags& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::addLogMessage& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::encrypt& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::decrypt& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::kdf& request, P&&);
template <class T, class P> template <class T, class P>
td::Status do_request(const T& request, P&& promise) { void make_request(T&& request, P&& promise) {
return td::Status::Error(400, "Function is unsupported"); auto status = do_request(std::forward<T>(request), std::move(promise));
if (status.is_error()) {
promise.operator()(std::move(status));
}
} }
td::Status set_config(object_ptr<tonlib_api::config> config); td::Status set_config(object_ptr<tonlib_api::config> config);
@ -138,6 +195,11 @@ class TonlibClient : public td::actor::Actor {
td::Status do_request(tonlib_api::options_setConfig& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise); td::Status do_request(tonlib_api::options_setConfig& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
td::Status do_request(const tonlib_api::raw_sendMessage& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise); td::Status do_request(const tonlib_api::raw_sendMessage& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
td::Status do_request(const tonlib_api::raw_createAndSendMessage& request,
td::Promise<object_ptr<tonlib_api::ok>>&& promise);
td::Status do_request(const tonlib_api::raw_createQuery& request,
td::Promise<object_ptr<tonlib_api::query_info>>&& promise);
td::Status do_request(tonlib_api::raw_getAccountState& request, td::Status do_request(tonlib_api::raw_getAccountState& request,
td::Promise<object_ptr<tonlib_api::raw_accountState>>&& promise); td::Promise<object_ptr<tonlib_api::raw_accountState>>&& promise);
td::Status do_request(tonlib_api::raw_getTransactions& request, td::Status do_request(tonlib_api::raw_getTransactions& request,
@ -191,6 +253,48 @@ class TonlibClient : public td::actor::Actor {
td::Status do_request(const tonlib_api::onLiteServerQueryError& request, td::Status do_request(const tonlib_api::onLiteServerQueryError& request,
td::Promise<object_ptr<tonlib_api::ok>>&& promise); td::Promise<object_ptr<tonlib_api::ok>>&& promise);
td::int64 next_query_id_{0};
std::map<td::int64, td::unique_ptr<Query>> queries_;
td::int64 register_query(td::unique_ptr<Query> query);
td::Result<tonlib_api::object_ptr<tonlib_api::query_info>> get_query_info(td::int64 id);
void finish_create_query(td::Result<td::unique_ptr<Query>> r_query,
td::Promise<object_ptr<tonlib_api::query_info>>&& promise);
void finish_send_query(td::Result<td::unique_ptr<Query>> r_query,
td::Promise<object_ptr<tonlib_api::sendGramsResult>>&& promise);
void query_estimate_fees(td::int64 id, bool ignore_chksig, td::Result<LastConfigState> r_state,
td::Promise<object_ptr<tonlib_api::query_fees>>&& promise);
td::Status do_request(const tonlib_api::query_getInfo& request,
td::Promise<object_ptr<tonlib_api::query_info>>&& promise);
td::Status do_request(const tonlib_api::query_estimateFees& request,
td::Promise<object_ptr<tonlib_api::query_fees>>&& promise);
td::Status do_request(const tonlib_api::query_send& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
td::Status do_request(tonlib_api::query_forget& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
td::Status do_request(tonlib_api::generic_createSendGramsQuery& request,
td::Promise<object_ptr<tonlib_api::query_info>>&& promise);
td::int64 next_smc_id_{0};
std::map<td::int64, td::unique_ptr<AccountState>> smcs_;
td::int64 register_smc(td::unique_ptr<AccountState> smc);
td::Result<tonlib_api::object_ptr<tonlib_api::smc_info>> get_smc_info(td::int64 id);
void finish_load_smc(td::unique_ptr<AccountState> query, td::Promise<object_ptr<tonlib_api::smc_info>>&& promise);
td::Status do_request(const tonlib_api::smc_load& request, td::Promise<object_ptr<tonlib_api::smc_info>>&& promise);
td::Status do_request(const tonlib_api::smc_getCode& request,
td::Promise<object_ptr<tonlib_api::tvm_cell>>&& promise);
td::Status do_request(const tonlib_api::smc_getData& request,
td::Promise<object_ptr<tonlib_api::tvm_cell>>&& promise);
td::Status do_request(const tonlib_api::smc_getState& request,
td::Promise<object_ptr<tonlib_api::tvm_cell>>&& promise);
td::Status do_request(int_api::GetAccountState request, td::Promise<td::unique_ptr<AccountState>>&&);
td::Status do_request(int_api::GetPrivateKey request, td::Promise<KeyStorage::PrivateKey>&&);
td::Status do_request(int_api::SendMessage request, td::Promise<td::Unit>&& promise);
td::Status do_request(const tonlib_api::liteServer_getInfo& request,
td::Promise<object_ptr<tonlib_api::liteServer_info>>&& promise);
void proxy_request(td::int64 query_id, std::string data); void proxy_request(td::int64 query_id, std::string data);
friend class TonlibQueryActor; friend class TonlibQueryActor;

View file

@ -27,6 +27,8 @@
// INVALID_MNEMONIC // INVALID_MNEMONIC
// INVALID_BAG_OF_CELLS // INVALID_BAG_OF_CELLS
// INVALID_PUBLIC_KEY // INVALID_PUBLIC_KEY
// INVALID_QUERY_ID
// INVALID_SMC_ID
// INVALID_ACCOUNT_ADDRESS // INVALID_ACCOUNT_ADDRESS
// INVALID_CONFIG // INVALID_CONFIG
// INVALID_PEM_KEY // INVALID_PEM_KEY
@ -65,6 +67,12 @@ struct TonlibError {
static td::Status InvalidAccountAddress() { static td::Status InvalidAccountAddress() {
return td::Status::Error(400, "INVALID_ACCOUNT_ADDRESS"); return td::Status::Error(400, "INVALID_ACCOUNT_ADDRESS");
} }
static td::Status InvalidQueryId() {
return td::Status::Error(400, "INVALID_QUERY_ID");
}
static td::Status InvalidSmcId() {
return td::Status::Error(400, "INVALID_SMC_ID");
}
static td::Status InvalidConfig(td::Slice reason) { static td::Status InvalidConfig(td::Slice reason) {
return td::Status::Error(400, PSLICE() << "INVALID_CONFIG: " << reason); return td::Status::Error(400, PSLICE() << "INVALID_CONFIG: " << reason);
} }
@ -110,6 +118,9 @@ struct TonlibError {
static td::Status ValidateTransactions() { static td::Status ValidateTransactions() {
return td::Status::Error(500, "VALIDATE_TRANSACTION"); return td::Status::Error(500, "VALIDATE_TRANSACTION");
} }
static td::Status ValidateConfig() {
return td::Status::Error(500, "VALIDATE_CONFIG");
}
static td::Status ValidateZeroState(td::Slice message) { static td::Status ValidateZeroState(td::Slice message) {
return td::Status::Error(500, PSLICE() << "VALIDATE_ZERO_STATE: " << message); return td::Status::Error(500, PSLICE() << "VALIDATE_ZERO_STATE: " << message);
} }

View file

@ -20,6 +20,57 @@
#include <iostream> #include <iostream>
#include <map> #include <map>
// Consider this as a TODO list:
//
// (from lite-client)
// SUPPORTED
// "time\tGet server time\n"
// "remote-version\tShows server time, version and capabilities\n"
// "help [<command>]\tThis help\n" // TODO: support [<command>]
// "quit\tExit\n";
// "sendfile <filename>\tLoad a serialized message from <filename> and send it to server\n"
//
// "saveaccount[code|data] <filename> <addr> [<block-id-ext>]\tSaves into specified file the most recent state "
// "runmethod <addr> <method-id> <params>...\tRuns GET method <method-id> of account <addr> "
// "with specified parameters\n"
//
// WONTSUPPORT
//
// UNSUPPORTED
//"last\tGet last block and state info from server\n"
//"status\tShow connection and local database status\n"
//"getaccount <addr> [<block-id-ext>]\tLoads the most recent state of specified account; <addr> is in "
//"[<workchain>:]<hex-or-base64-addr> format\n"
//"(StateInit) or just the code or data of specified account; <addr> is in "
//"[<workchain>:]<hex-or-base64-addr> format\n"
//"allshards [<block-id-ext>]\tShows shard configuration from the most recent masterchain "
//"state or from masterchain state corresponding to <block-id-ext>\n"
//"getconfig [<param>...]\tShows specified or all configuration parameters from the latest masterchain state\n"
//"getconfigfrom <block-id-ext> [<param>...]\tShows specified or all configuration parameters from the "
//"masterchain state of <block-id-ext>\n"
//"saveconfig <filename> [<block-id-ext>]\tSaves all configuration parameters into specified file\n"
//"gethead <block-id-ext>\tShows block header for <block-id-ext>\n"
//"getblock <block-id-ext>\tDownloads block\n"
//"dumpblock <block-id-ext>\tDownloads and dumps specified block\n"
//"getstate <block-id-ext>\tDownloads state corresponding to specified block\n"
//"dumpstate <block-id-ext>\tDownloads and dumps state corresponding to specified block\n"
//"dumptrans <block-id-ext> <account-id> <trans-lt>\tDumps one transaction of specified account\n"
//"lasttrans[dump] <account-id> <trans-lt> <trans-hash> [<count>]\tShows or dumps specified transaction and "
//"several preceding "
//"ones\n"
//"listblocktrans[rev] <block-id-ext> <count> [<start-account-id> <start-trans-lt>]\tLists block transactions, "
//"starting immediately after or before the specified one\n"
//"blkproofchain[step] <from-block-id-ext> [<to-block-id-ext>]\tDownloads and checks proof of validity of the /"second "
//"indicated block (or the last known masterchain block) starting from given block\n"
//"byseqno <workchain> <shard-prefix> <seqno>\tLooks up a block by workchain, shard and seqno, and shows its "
//"header\n"
//"bylt <workchain> <shard-prefix> <lt>\tLooks up a block by workchain, shard and logical time, and shows its "
//"header\n"
//"byutime <workchain> <shard-prefix> <utime>\tLooks up a block by workchain, shard and creation time, and "
//"shows its header\n"
//"known\tShows the list of all known block ids\n"
//"privkey <filename>\tLoads a private key from file\n"
class TonlibCli : public td::actor::Actor { class TonlibCli : public td::actor::Actor {
public: public:
struct Options { struct Options {
@ -201,8 +252,25 @@ class TonlibCli : public td::actor::Actor {
} }
return true; return true;
}; };
td::Promise<td::Unit> cmd_promise = [line = line.clone()](td::Result<td::Unit> res) {
if (res.is_ok()) {
// on_ok
} else {
td::TerminalIO::out() << "Query {" << line.as_slice() << "} FAILED: \n\t" << res.error() << "\n";
}
};
if (cmd == "help") { if (cmd == "help") {
td::TerminalIO::out() << "help - show this help\n"; td::TerminalIO::out() << "help\tThis help\n";
td::TerminalIO::out() << "time\tGet server time\n";
td::TerminalIO::out() << "remote-version\tShows server time, version and capabilities\n";
td::TerminalIO::out() << "sendfile <filename>\tLoad a serialized message from <filename> and send it to server\n";
td::TerminalIO::out() << "exit\tExit\n";
td::TerminalIO::out() << "quit\tExit\n";
td::TerminalIO::out()
<< "saveaccount[code|data] <filename> <addr>\tSaves into specified file the most recent state\n";
td::TerminalIO::out() << "genkey - generate new secret key\n"; td::TerminalIO::out() << "genkey - generate new secret key\n";
td::TerminalIO::out() << "keys - show all stored keys\n"; td::TerminalIO::out() << "keys - show all stored keys\n";
td::TerminalIO::out() << "unpackaddress <address> - validate and parse address\n"; td::TerminalIO::out() << "unpackaddress <address> - validate and parse address\n";
@ -210,6 +278,7 @@ class TonlibCli : public td::actor::Actor {
td::TerminalIO::out() << "importkey - import key\n"; td::TerminalIO::out() << "importkey - import key\n";
td::TerminalIO::out() << "deletekeys - delete ALL PRIVATE KEYS\n"; td::TerminalIO::out() << "deletekeys - delete ALL PRIVATE KEYS\n";
td::TerminalIO::out() << "exportkey [<key_id>] - export key\n"; td::TerminalIO::out() << "exportkey [<key_id>] - export key\n";
td::TerminalIO::out() << "exportkeypem [<key_id>] - export key\n";
td::TerminalIO::out() << "setconfig <path> [<name>] [<use_callback>] [<force>] - set lite server config\n"; td::TerminalIO::out() << "setconfig <path> [<name>] [<use_callback>] [<force>] - set lite server config\n";
td::TerminalIO::out() << "getstate <key_id> - get state of simple wallet with requested key\n"; td::TerminalIO::out() << "getstate <key_id> - get state of simple wallet with requested key\n";
td::TerminalIO::out() td::TerminalIO::out()
@ -219,10 +288,9 @@ class TonlibCli : public td::actor::Actor {
"<from_key_id> to <to_key_id>.\n" "<from_key_id> to <to_key_id>.\n"
<< "\t<from_key_id> could also be 'giver'\n" << "\t<from_key_id> could also be 'giver'\n"
<< "\t<to_key_id> could also be 'giver' or smartcontract address\n"; << "\t<to_key_id> could also be 'giver' or smartcontract address\n";
td::TerminalIO::out() << "exit - exit from this programm\n";
} else if (cmd == "genkey") { } else if (cmd == "genkey") {
generate_key(); generate_key();
} else if (cmd == "exit") { } else if (cmd == "exit" || cmd == "quit") {
is_closing_ = true; is_closing_ = true;
client_.reset(); client_.reset();
ref_cnt_--; ref_cnt_--;
@ -233,8 +301,8 @@ class TonlibCli : public td::actor::Actor {
//delete_key(parser.read_word()); //delete_key(parser.read_word());
} else if (cmd == "deletekeys") { } else if (cmd == "deletekeys") {
delete_all_keys(); delete_all_keys();
} else if (cmd == "exportkey") { } else if (cmd == "exportkey" || cmd == "exportkeypem") {
export_key(parser.read_word()); export_key(cmd.str(), parser.read_word());
} else if (cmd == "importkey") { } else if (cmd == "importkey") {
import_key(parser.read_all()); import_key(parser.read_all());
} else if (cmd == "setconfig") { } else if (cmd == "setconfig") {
@ -265,20 +333,93 @@ class TonlibCli : public td::actor::Actor {
set_bounceable(addr, to_bool(bounceable, true)); set_bounceable(addr, to_bool(bounceable, true));
} else if (cmd == "netstats") { } else if (cmd == "netstats") {
dump_netstats(); dump_netstats();
// reviewed from here
} else if (cmd == "sync") { } else if (cmd == "sync") {
sync(); sync(std::move(cmd_promise));
} else if (cmd == "time") {
remote_time(std::move(cmd_promise));
} else if (cmd == "remote-version") {
remote_version(std::move(cmd_promise));
} else if (cmd == "sendfile") {
send_file(parser.read_word(), std::move(cmd_promise));
} else if (cmd == "saveaccount" || cmd == "saveaccountdata" || cmd == "saveaccountcode") {
auto path = parser.read_word();
auto address = parser.read_word();
save_account(cmd, path, address, std::move(cmd_promise));
} else {
cmd_promise.set_error(td::Status::Error(PSLICE() << "Unkwnown query `" << cmd << "`"));
}
if (cmd_promise) {
cmd_promise.set_value(td::Unit());
} }
} }
void sync() { void remote_time(td::Promise<td::Unit> promise) {
using tonlib_api::make_object; send_query(tonlib_api::make_object<tonlib_api::liteServer_getInfo>(), promise.wrap([](auto&& info) {
send_query(make_object<tonlib_api::sync>(), [](auto r_ok) { td::TerminalIO::out() << "Lite server time is: " << info->now_ << "\n";
LOG_IF(ERROR, r_ok.is_error()) << r_ok.error(); return td::Unit();
if (r_ok.is_ok()) { }));
td::TerminalIO::out() << "synchronized\n";
}
});
} }
void remote_version(td::Promise<td::Unit> promise) {
send_query(tonlib_api::make_object<tonlib_api::liteServer_getInfo>(), promise.wrap([](auto&& info) {
td::TerminalIO::out() << "Lite server time is: " << info->now_ << "\n";
td::TerminalIO::out() << "Lite server version is: " << info->version_ << "\n";
td::TerminalIO::out() << "Lite server capabilities are: " << info->capabilities_ << "\n";
return td::Unit();
}));
}
void send_file(td::Slice name, td::Promise<td::Unit> promise) {
TRY_RESULT_PROMISE(promise, data, td::read_file_str(name.str()));
send_query(tonlib_api::make_object<tonlib_api::raw_sendMessage>(std::move(data)), promise.wrap([](auto&& info) {
td::TerminalIO::out() << "Query was sent\n";
return td::Unit();
}));
}
void save_account(td::Slice cmd, td::Slice path, td::Slice address, td::Promise<td::Unit> promise) {
TRY_RESULT_PROMISE(promise, addr, to_account_address(address, false));
send_query(tonlib_api::make_object<tonlib_api::smc_load>(std::move(addr.address)),
promise.send_closure(actor_id(this), &TonlibCli::save_account_2, cmd.str(), path.str(), address.str()));
}
void save_account_2(std::string cmd, std::string path, std::string address,
tonlib_api::object_ptr<tonlib_api::smc_info> info, td::Promise<td::Unit> promise) {
auto with_query = [&, self = this](auto query, auto log) {
send_query(std::move(query),
promise.send_closure(actor_id(self), &TonlibCli::save_account_3, std::move(path), std::move(log)));
};
if (cmd == "saveaccount") {
with_query(tonlib_api::make_object<tonlib_api::smc_getState>(info->id_),
PSTRING() << "StateInit of account " << address);
} else if (cmd == "saveaccountcode") {
with_query(tonlib_api::make_object<tonlib_api::smc_getCode>(info->id_), PSTRING()
<< "Code of account " << address);
} else if (cmd == "saveaccountdata") {
with_query(tonlib_api::make_object<tonlib_api::smc_getData>(info->id_), PSTRING()
<< "Data of account " << address);
} else {
promise.set_error(td::Status::Error("Unknown query"));
}
}
void save_account_3(std::string path, std::string log, tonlib_api::object_ptr<tonlib_api::tvm_cell> cell,
td::Promise<td::Unit> promise) {
TRY_STATUS_PROMISE(promise, td::write_file(path, cell->bytes_));
td::TerminalIO::out() << log << " was successfully written to the disk(" << td::format::as_size(cell->bytes_.size())
<< ")\n";
promise.set_value(td::Unit());
}
void sync(td::Promise<td::Unit> promise) {
using tonlib_api::make_object;
send_query(make_object<tonlib_api::sync>(), promise.wrap([](auto&&) {
td::TerminalIO::out() << "synchronized\n";
return td::Unit();
}));
}
void dump_netstats() { void dump_netstats() {
td::TerminalIO::out() << td::tag("snd", td::format::as_size(snd_bytes_)) << "\n"; td::TerminalIO::out() << td::tag("snd", td::format::as_size(snd_bytes_)) << "\n";
td::TerminalIO::out() << td::tag("rcv", td::format::as_size(rcv_bytes_)) << "\n"; td::TerminalIO::out() << td::tag("rcv", td::format::as_size(rcv_bytes_)) << "\n";
@ -298,15 +439,41 @@ class TonlibCli : public td::actor::Actor {
void on_tonlib_result(std::uint64_t id, tonlib_api::object_ptr<tonlib_api::Object> result) { void on_tonlib_result(std::uint64_t id, tonlib_api::object_ptr<tonlib_api::Object> result) {
if (id == 0) { if (id == 0) {
if (result->get_id() == tonlib_api::updateSendLiteServerQuery::ID) { switch (result->get_id()) {
auto update = tonlib_api::move_object_as<tonlib_api::updateSendLiteServerQuery>(std::move(result)); case tonlib_api::updateSendLiteServerQuery::ID: {
CHECK(!raw_client_.empty()); auto update = tonlib_api::move_object_as<tonlib_api::updateSendLiteServerQuery>(std::move(result));
snd_bytes_ += update->data_.size(); CHECK(!raw_client_.empty());
send_closure(raw_client_, &ton::adnl::AdnlExtClient::send_query, "query", td::BufferSlice(update->data_), snd_bytes_ += update->data_.size();
td::Timestamp::in(5), send_closure(raw_client_, &ton::adnl::AdnlExtClient::send_query, "query", td::BufferSlice(update->data_),
[actor_id = actor_id(this), id = update->id_](td::Result<td::BufferSlice> res) { td::Timestamp::in(5),
send_closure(actor_id, &TonlibCli::on_adnl_result, id, std::move(res)); [actor_id = actor_id(this), id = update->id_](td::Result<td::BufferSlice> res) {
}); send_closure(actor_id, &TonlibCli::on_adnl_result, id, std::move(res));
});
return;
}
case tonlib_api::updateSyncState::ID: {
auto update = tonlib_api::move_object_as<tonlib_api::updateSyncState>(std::move(result));
switch (update->sync_state_->get_id()) {
case tonlib_api::syncStateDone::ID: {
td::TerminalIO::out() << "synchronization: DONE\n";
break;
}
case tonlib_api::syncStateInProgress::ID: {
auto progress = tonlib_api::move_object_as<tonlib_api::syncStateInProgress>(update->sync_state_);
auto from = progress->from_seqno_;
auto to = progress->to_seqno_;
auto at = progress->current_seqno_;
auto d = to - from;
if (d <= 0) {
td::TerminalIO::out() << "synchronization: ???\n";
} else {
td::TerminalIO::out() << "synchronization: " << 100 * (at - from) / d << "%\n";
}
break;
}
}
return;
}
} }
} }
auto it = query_handlers_.find(id); auto it = query_handlers_.find(id);
@ -407,7 +574,7 @@ class TonlibCli : public td::actor::Actor {
info.public_key = key->public_key_; info.public_key = key->public_key_;
info.secret = std::move(key->secret_); info.secret = std::move(key->secret_);
keys_.push_back(std::move(info)); keys_.push_back(std::move(info));
export_key(key->public_key_, keys_.size() - 1, std::move(password)); export_key("exportkey", key->public_key_, keys_.size() - 1, std::move(password));
store_keys(); store_keys();
}); });
} }
@ -596,11 +763,11 @@ class TonlibCli : public td::actor::Actor {
td::TerminalIO::out() << "Ok\n"; td::TerminalIO::out() << "Ok\n";
}); });
} }
void export_key(td::Slice key) { void export_key(std::string cmd, td::Slice key) {
if (key.empty()) { if (key.empty()) {
dump_keys(); dump_keys();
td::TerminalIO::out() << "Choose public key (hex prefix or #N)"; td::TerminalIO::out() << "Choose public key (hex prefix or #N)";
cont_ = [this](td::Slice key) { this->export_key(key); }; cont_ = [this, cmd](td::Slice key) { this->export_key(cmd, key); };
return; return;
} }
auto r_key_i = to_key_i(key); auto r_key_i = to_key_i(key);
@ -614,24 +781,40 @@ class TonlibCli : public td::actor::Actor {
<< "public key: " << td::buffer_to_hex(keys_[key_i].public_key) << "\n"; << "public key: " << td::buffer_to_hex(keys_[key_i].public_key) << "\n";
td::TerminalIO::out() << "Enter password (could be empty)"; td::TerminalIO::out() << "Enter password (could be empty)";
cont_ = [this, key = key.str(), key_i](td::Slice password) { this->export_key(key, key_i, password); }; cont_ = [this, cmd, key = key.str(), key_i](td::Slice password) { this->export_key(cmd, key, key_i, password); };
} }
void export_key(std::string key, size_t key_i, td::Slice password) { void export_key(std::string cmd, std::string key, size_t key_i, td::Slice password) {
using tonlib_api::make_object; using tonlib_api::make_object;
send_query(make_object<tonlib_api::exportKey>(make_object<tonlib_api::inputKey>( if (cmd == "exportkey") {
make_object<tonlib_api::key>(keys_[key_i].public_key, keys_[key_i].secret.copy()), send_query(make_object<tonlib_api::exportKey>(make_object<tonlib_api::inputKeyRegular>(
td::SecureString(password))), make_object<tonlib_api::key>(keys_[key_i].public_key, keys_[key_i].secret.copy()),
[this, key = std::move(key), key_i](auto r_res) { td::SecureString(password))),
if (r_res.is_error()) { [this, key = std::move(key), key_i](auto r_res) {
td::TerminalIO::out() << "Can't export key id: [" << key << "] " << r_res.error() << "\n"; if (r_res.is_error()) {
return; td::TerminalIO::out() << "Can't export key id: [" << key << "] " << r_res.error() << "\n";
} return;
dump_key(key_i); }
for (auto& word : r_res.ok()->word_list_) { dump_key(key_i);
td::TerminalIO::out() << " " << word.as_slice() << "\n"; for (auto& word : r_res.ok()->word_list_) {
} td::TerminalIO::out() << " " << word.as_slice() << "\n";
}); }
});
} else {
send_query(make_object<tonlib_api::exportPemKey>(
make_object<tonlib_api::inputKeyRegular>(
make_object<tonlib_api::key>(keys_[key_i].public_key, keys_[key_i].secret.copy()),
td::SecureString(password)),
td::SecureString("cucumber")),
[this, key = std::move(key), key_i](auto r_res) {
if (r_res.is_error()) {
td::TerminalIO::out() << "Can't export key id: [" << key << "] " << r_res.error() << "\n";
return;
}
dump_key(key_i);
td::TerminalIO::out() << "\n" << r_res.ok()->pem_.as_slice() << "\n";
});
}
} }
void import_key(td::Slice slice, std::vector<td::SecureString> words = {}) { void import_key(td::Slice slice, std::vector<td::SecureString> words = {}) {
@ -669,7 +852,7 @@ class TonlibCli : public td::actor::Actor {
info.public_key = key->public_key_; info.public_key = key->public_key_;
info.secret = std::move(key->secret_); info.secret = std::move(key->secret_);
keys_.push_back(std::move(info)); keys_.push_back(std::move(info));
export_key(key->public_key_, keys_.size() - 1, std::move(password)); export_key("exportkey", key->public_key_, keys_.size() - 1, std::move(password));
store_keys(); store_keys();
}); });
} }
@ -830,21 +1013,42 @@ class TonlibCli : public td::actor::Actor {
} }
using tonlib_api::make_object; using tonlib_api::make_object;
auto key = !from.secret.empty() auto key = !from.secret.empty()
? make_object<tonlib_api::inputKey>( ? make_object<tonlib_api::inputKeyRegular>(
make_object<tonlib_api::key>(from.public_key, from.secret.copy()), td::SecureString(password)) make_object<tonlib_api::key>(from.public_key, from.secret.copy()), td::SecureString(password))
: nullptr; : nullptr;
send_query( send_query(make_object<tonlib_api::generic_createSendGramsQuery>(std::move(key), std::move(from.address),
make_object<tonlib_api::generic_sendGrams>(std::move(key), std::move(from.address), std::move(to.address), std::move(to.address), grams, 60,
grams, 60, allow_send_to_uninited, std::move(msg)), allow_send_to_uninited, std::move(msg)),
[this](auto r_res) { [self = this](auto r_res) {
if (r_res.is_error()) { if (r_res.is_error()) {
td::TerminalIO::out() << "Can't transfer: " << r_res.error() << "\n"; td::TerminalIO::out() << "Can't transfer: " << r_res.error() << "\n";
on_error(); self->on_error();
return; return;
} }
td::TerminalIO::out() << to_string(r_res.ok()); td::TerminalIO::out() << to_string(r_res.ok());
on_ok(); self->send_query(make_object<tonlib_api::query_estimateFees>(r_res.ok()->id_, false),
}); [self](auto r_res) {
if (r_res.is_error()) {
td::TerminalIO::out() << "Can't transfer: " << r_res.error() << "\n";
self->on_error();
return;
}
td::TerminalIO::out() << to_string(r_res.ok());
self->on_ok();
});
self->send_query(make_object<tonlib_api::query_send>(r_res.ok()->id_), [self](auto r_res) {
if (r_res.is_error()) {
td::TerminalIO::out() << "Can't transfer: " << r_res.error() << "\n";
self->on_error();
return;
}
td::TerminalIO::out() << to_string(r_res.ok());
self->on_ok();
});
self->on_ok();
});
} }
void init_simple_wallet(td::Slice key) { void init_simple_wallet(td::Slice key) {
@ -871,7 +1075,7 @@ class TonlibCli : public td::actor::Actor {
void init_simple_wallet(std::string key, size_t key_i, td::Slice password) { void init_simple_wallet(std::string key, size_t key_i, td::Slice password) {
using tonlib_api::make_object; using tonlib_api::make_object;
if (options_.use_simple_wallet) { if (options_.use_simple_wallet) {
send_query(make_object<tonlib_api::testWallet_init>(make_object<tonlib_api::inputKey>( send_query(make_object<tonlib_api::testWallet_init>(make_object<tonlib_api::inputKeyRegular>(
make_object<tonlib_api::key>(keys_[key_i].public_key, keys_[key_i].secret.copy()), make_object<tonlib_api::key>(keys_[key_i].public_key, keys_[key_i].secret.copy()),
td::SecureString(password))), td::SecureString(password))),
[key = std::move(key)](auto r_res) { [key = std::move(key)](auto r_res) {
@ -882,7 +1086,7 @@ class TonlibCli : public td::actor::Actor {
td::TerminalIO::out() << to_string(r_res.ok()); td::TerminalIO::out() << to_string(r_res.ok());
}); });
} else { } else {
send_query(make_object<tonlib_api::wallet_init>(make_object<tonlib_api::inputKey>( send_query(make_object<tonlib_api::wallet_init>(make_object<tonlib_api::inputKeyRegular>(
make_object<tonlib_api::key>(keys_[key_i].public_key, keys_[key_i].secret.copy()), make_object<tonlib_api::key>(keys_[key_i].public_key, keys_[key_i].secret.copy()),
td::SecureString(password))), td::SecureString(password))),
[key = std::move(key)](auto r_res) { [key = std::move(key)](auto r_res) {

View file

@ -20,20 +20,9 @@
#include "td/utils/misc.h" #include "td/utils/misc.h"
#include "vm/cellslice.h" #include "vm/cellslice.h"
namespace tonlib { namespace tonlib {
int VERBOSITY_NAME(tonlib_query) = VERBOSITY_NAME(INFO); int VERBOSITY_NAME(tonlib_query) = VERBOSITY_NAME(DEBUG);
int VERBOSITY_NAME(last_block) = VERBOSITY_NAME(INFO); int VERBOSITY_NAME(last_block) = VERBOSITY_NAME(DEBUG);
int VERBOSITY_NAME(lite_server) = VERBOSITY_NAME(INFO); int VERBOSITY_NAME(last_config) = VERBOSITY_NAME(DEBUG);
int VERBOSITY_NAME(lite_server) = VERBOSITY_NAME(DEBUG);
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 } // namespace tonlib

View file

@ -23,19 +23,8 @@
#include "block/block-parse.h" #include "block/block-parse.h"
namespace tonlib { namespace tonlib {
template <class F>
auto try_f(F&& f) noexcept -> decltype(f()) {
try {
return f();
} catch (vm::VmError error) {
return td::Status::Error(PSLICE() << "Got a vm exception: " << error.get_msg());
}
}
#define TRY_VM(f) try_f([&] { return f; })
extern int VERBOSITY_NAME(tonlib_query); extern int VERBOSITY_NAME(tonlib_query);
extern int VERBOSITY_NAME(last_block); extern int VERBOSITY_NAME(last_block);
extern int VERBOSITY_NAME(last_config);
extern int VERBOSITY_NAME(lite_server); extern int VERBOSITY_NAME(lite_server);
td::Result<td::Ref<vm::CellSlice>> binary_bitstring_to_cellslice(td::Slice literal);
} // namespace tonlib } // namespace tonlib

View file

@ -10,6 +10,8 @@ add_subdirectory(impl)
set(VALIDATOR_DB_SOURCE set(VALIDATOR_DB_SOURCE
db/archiver.cpp db/archiver.cpp
db/archiver.hpp db/archiver.hpp
db/archive-db.cpp
db/archive-db.hpp
db/blockdb.cpp db/blockdb.cpp
db/blockdb.hpp db/blockdb.hpp
db/celldb.cpp db/celldb.cpp
@ -25,6 +27,9 @@ set(VALIDATOR_DB_SOURCE
db/statedb.cpp db/statedb.cpp
db/staticfilesdb.cpp db/staticfilesdb.cpp
db/staticfilesdb.hpp db/staticfilesdb.hpp
db/package.hpp
db/package.cpp
) )
set(VALIDATOR_HEADERS set(VALIDATOR_HEADERS

View file

@ -59,6 +59,7 @@ struct BlockHandleImpl : public BlockHandleInterface {
dbf_moved = 0x1000000, dbf_moved = 0x1000000,
dbf_deleted = 0x2000000, dbf_deleted = 0x2000000,
dbf_deleted_boc = 0x4000000, dbf_deleted_boc = 0x4000000,
dbf_moved_new = 0x8000000,
dbf_processed = 0x10000000, dbf_processed = 0x10000000,
}; };
@ -95,6 +96,9 @@ struct BlockHandleImpl : public BlockHandleInterface {
bool moved_to_storage() const override { bool moved_to_storage() const override {
return flags_.load(std::memory_order_consume) & Flags::dbf_moved; return flags_.load(std::memory_order_consume) & Flags::dbf_moved;
} }
bool moved_to_archive() const override {
return flags_.load(std::memory_order_consume) & Flags::dbf_moved_new;
}
bool deleted() const override { bool deleted() const override {
return flags_.load(std::memory_order_consume) & Flags::dbf_deleted; return flags_.load(std::memory_order_consume) & Flags::dbf_deleted;
} }
@ -393,6 +397,15 @@ struct BlockHandleImpl : public BlockHandleInterface {
flags_ |= Flags::dbf_moved; flags_ |= Flags::dbf_moved;
unlock(); unlock();
} }
void set_moved_to_archive() override {
if (flags_.load(std::memory_order_consume) & Flags::dbf_moved_new) {
return;
}
lock();
flags_ |= Flags::dbf_moved_new;
flags_ &= ~Flags::dbf_moved;
unlock();
}
void set_deleted() override { void set_deleted() override {
if (flags_.load(std::memory_order_consume) & Flags::dbf_deleted) { if (flags_.load(std::memory_order_consume) & Flags::dbf_deleted) {
return; return;

332
validator/db/archive-db.cpp Normal file
View file

@ -0,0 +1,332 @@
#include "archive-db.hpp"
#include "common/errorcode.h"
#include "common/int-to-string.hpp"
#include "files-async.hpp"
#include "td/db/RocksDb.h"
#include "validator/fabric.h"
namespace ton {
namespace validator {
void PackageWriter::append(std::string filename, td::BufferSlice data,
td::Promise<std::pair<td::uint64, td::uint64>> promise) {
auto offset = package_->append(std::move(filename), std::move(data));
auto size = package_->size();
promise.set_value(std::pair<td::uint64, td::uint64>{offset, size});
}
class PackageReader : public td::actor::Actor {
public:
PackageReader(std::shared_ptr<Package> package, td::uint64 offset,
td::Promise<std::pair<std::string, td::BufferSlice>> promise)
: package_(std::move(package)), offset_(offset), promise_(std::move(promise)) {
}
void start_up() {
promise_.set_result(package_->read(offset_));
}
private:
std::shared_ptr<Package> package_;
td::uint64 offset_;
td::Promise<std::pair<std::string, td::BufferSlice>> promise_;
};
void ArchiveFile::start_up() {
auto R = Package::open(path_, false, true);
if (R.is_error()) {
LOG(FATAL) << "failed to open/create archive '" << path_ << "': " << R.move_as_error();
return;
}
package_ = std::make_shared<Package>(R.move_as_ok());
index_ = std::make_shared<td::RocksDb>(td::RocksDb::open(path_ + ".index").move_as_ok());
std::string value;
auto R2 = index_->get("status", value);
R2.ensure();
if (R2.move_as_ok() == td::KeyValue::GetStatus::Ok) {
auto len = td::to_integer<td::uint64>(value);
package_->truncate(len);
} else {
package_->truncate(0);
}
writer_ = td::actor::create_actor<PackageWriter>("writer", package_);
}
void ArchiveFile::write(FileDb::RefId ref_id, td::BufferSlice data, td::Promise<td::Unit> promise) {
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), ref_id, promise = std::move(promise)](
td::Result<std::pair<td::uint64, td::uint64>> R) mutable {
if (R.is_error()) {
promise.set_error(R.move_as_error());
return;
}
auto v = R.move_as_ok();
td::actor::send_closure(SelfId, &ArchiveFile::completed_write, std::move(ref_id), v.first, v.second,
std::move(promise));
});
FileHash hash;
ref_id.visit([&](const auto &obj) { hash = obj.hash(); });
td::actor::send_closure(writer_, &PackageWriter::append, hash.to_hex(), std::move(data), std::move(P));
}
void ArchiveFile::write_handle(BlockHandle handle, td::Promise<td::Unit> promise) {
FileHash hash = fileref::BlockInfo{handle->id()}.hash();
index_->begin_transaction().ensure();
do {
auto version = handle->version();
index_->set(hash.to_hex(), handle->serialize().as_slice().str()).ensure();
handle->flushed_upto(version);
} while (handle->need_flush());
index_->commit_transaction().ensure();
promise.set_value(td::Unit());
}
void ArchiveFile::completed_write(FileDb::RefId ref_id, td::uint64 offset, td::uint64 new_size,
td::Promise<td::Unit> promise) {
FileHash hash;
ref_id.visit([&](const auto &obj) { hash = obj.hash(); });
index_->begin_transaction().ensure();
index_->set("status", td::to_string(new_size)).ensure();
index_->set(hash.to_hex(), td::to_string(offset)).ensure();
index_->commit_transaction().ensure();
promise.set_value(td::Unit());
}
void ArchiveFile::read(FileDb::RefId ref_id, td::Promise<td::BufferSlice> promise) {
FileHash hash;
ref_id.visit([&](const auto &obj) { hash = obj.hash(); });
std::string value;
auto R = index_->get(hash.to_hex(), value);
R.ensure();
if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
promise.set_error(td::Status::Error(ErrorCode::notready, "not in db (archive)"));
return;
}
auto offset = td::to_integer<td::uint64>(value);
auto P = td::PromiseCreator::lambda(
[promise = std::move(promise)](td::Result<std::pair<std::string, td::BufferSlice>> R) mutable {
if (R.is_error()) {
promise.set_error(R.move_as_error());
} else {
promise.set_value(std::move(R.move_as_ok().second));
}
});
td::actor::create_actor<PackageReader>("reader", package_, offset, std::move(P)).release();
}
void ArchiveFile::read_handle(BlockIdExt block_id, td::Promise<BlockHandle> promise) {
FileHash hash = fileref::BlockInfo{block_id}.hash();
std::string value;
auto R = index_->get(hash.to_hex(), value);
R.ensure();
if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
promise.set_error(td::Status::Error(ErrorCode::notready, "not in archive db"));
return;
}
promise.set_result(create_block_handle(td::BufferSlice{value}));
}
ArchiveManager::ArchiveManager(std::string db_root) : db_root_(db_root) {
}
void ArchiveManager::write(UnixTime ts, bool key_block, FileDb::RefId ref_id, td::BufferSlice data,
td::Promise<td::Unit> promise) {
auto f = get_file(ts, key_block);
td::actor::send_closure(f->file_actor_id(), &ArchiveFile::write, std::move(ref_id), std::move(data),
std::move(promise));
}
void ArchiveManager::write_handle(BlockHandle handle, td::Promise<td::Unit> promise) {
auto f = get_file(handle->unix_time(), handle->is_key_block());
update_desc(*f, handle->id().shard_full(), handle->id().seqno(), handle->logical_time());
td::actor::send_closure(f->file_actor_id(), &ArchiveFile::write_handle, std::move(handle), std::move(promise));
}
void ArchiveManager::read(UnixTime ts, bool key_block, FileDb::RefId ref_id, td::Promise<td::BufferSlice> promise) {
auto f = get_file(ts, key_block);
td::actor::send_closure(f->file_actor_id(), &ArchiveFile::read, std::move(ref_id), std::move(promise));
}
void ArchiveManager::read_handle(BlockIdExt block_id, td::Promise<BlockHandle> promise) {
if (block_id.is_masterchain()) {
auto f = get_file_by_seqno(block_id.shard_full(), block_id.seqno(), true);
if (!f) {
read_handle_cont(block_id, std::move(promise));
return;
}
auto P = td::PromiseCreator::lambda(
[SelfId = actor_id(this), block_id, promise = std::move(promise)](td::Result<BlockHandle> R) mutable {
if (R.is_error()) {
td::actor::send_closure(SelfId, &ArchiveManager::read_handle_cont, block_id, std::move(promise));
} else {
promise.set_value(R.move_as_ok());
}
});
td::actor::send_closure(f->file_actor_id(), &ArchiveFile::read_handle, block_id, std::move(P));
} else {
read_handle_cont(block_id, std::move(promise));
}
}
void ArchiveManager::read_handle_cont(BlockIdExt block_id, td::Promise<BlockHandle> promise) {
auto f = get_file_by_seqno(block_id.shard_full(), block_id.seqno(), false);
if (!f) {
promise.set_error(td::Status::Error(ErrorCode::notready, "not in archive db"));
return;
}
td::actor::send_closure(f->file_actor_id(), &ArchiveFile::read_handle, block_id, std::move(promise));
}
UnixTime ArchiveManager::convert_ts(UnixTime ts, bool key_block) {
if (!key_block) {
return ts - (ts % (1 << 17));
} else {
return ts - (ts % (1 << 22));
}
}
ArchiveManager::FileDescription *ArchiveManager::get_file(UnixTime ts, bool key_block, bool force) {
ts = convert_ts(ts, key_block);
auto &f = key_block ? key_files_ : files_;
auto it = f.find(ts);
if (it != f.end()) {
return &it->second;
}
if (!force) {
return nullptr;
}
return add_file(ts, key_block);
}
ArchiveManager::FileDescription *ArchiveManager::add_file(UnixTime ts, bool key_block) {
CHECK((key_block ? key_files_ : files_).count(ts) == 0);
index_->begin_transaction().ensure();
{
std::vector<td::int32> t;
std::vector<td::int32> tk;
for (auto &e : files_) {
t.push_back(e.first);
}
for (auto &e : key_files_) {
tk.push_back(e.first);
}
(key_block ? tk : t).push_back(ts);
index_
->set(create_serialize_tl_object<ton_api::db_archive_index_key>().as_slice(),
create_serialize_tl_object<ton_api::db_archive_index_value>(std::move(t), std::move(tk)).as_slice())
.ensure();
}
{
index_
->set(create_serialize_tl_object<ton_api::db_archive_package_key>(ts, key_block).as_slice(),
create_serialize_tl_object<ton_api::db_archive_package_value>(
ts, key_block, std::vector<tl_object_ptr<ton_api::db_archive_package_firstBlock>>(), false)
.as_slice())
.ensure();
}
index_->commit_transaction().ensure();
FileDescription desc{ts, key_block};
auto w = td::actor::create_actor<ArchiveFile>(
PSTRING() << "archivefile" << ts,
PSTRING() << db_root_ << "/packed/" << (key_block ? "key" : "") << ts << ".pack", ts);
desc.file = std::move(w);
return &(key_block ? key_files_ : files_).emplace(ts, std::move(desc)).first->second;
}
void ArchiveManager::load_package(UnixTime ts, bool key_block) {
auto key = create_serialize_tl_object<ton_api::db_archive_package_key>(ts, key_block);
std::string value;
auto v = index_->get(key.as_slice(), value);
v.ensure();
CHECK(v.move_as_ok() == td::KeyValue::GetStatus::Ok);
auto R = fetch_tl_object<ton_api::db_archive_package_value>(value, true);
R.ensure();
auto x = R.move_as_ok();
if (x->deleted_) {
return;
}
FileDescription desc{ts, key_block};
for (auto &e : x->firstblocks_) {
desc.first_blocks[ShardIdFull{e->workchain_, static_cast<ShardId>(e->shard_)}] =
FileDescription::Desc{static_cast<td::uint32>(e->seqno_), static_cast<LogicalTime>(e->lt_)};
}
desc.file = td::actor::create_actor<ArchiveFile>(
PSTRING() << "archivefile" << ts,
PSTRING() << db_root_ << "/packed/" << (key_block ? "key" : "") << ts << ".pack", ts);
(key_block ? key_files_ : files_).emplace(ts, std::move(desc));
}
void ArchiveManager::update_desc(FileDescription &desc, ShardIdFull shard, BlockSeqno seqno, LogicalTime lt) {
auto it = desc.first_blocks.find(shard);
if (it != desc.first_blocks.end() && it->second.seqno <= seqno) {
return;
}
desc.first_blocks[shard] = FileDescription::Desc{seqno, lt};
std::vector<tl_object_ptr<ton_api::db_archive_package_firstBlock>> vec;
for (auto &e : desc.first_blocks) {
vec.push_back(create_tl_object<ton_api::db_archive_package_firstBlock>(e.first.workchain, e.first.shard,
e.second.seqno, e.second.lt));
}
index_->begin_transaction().ensure();
index_
->set(create_serialize_tl_object<ton_api::db_archive_package_key>(desc.unix_time, desc.key_block).as_slice(),
create_serialize_tl_object<ton_api::db_archive_package_value>(desc.unix_time, desc.key_block,
std::move(vec), false)
.as_slice())
.ensure();
index_->commit_transaction().ensure();
}
ArchiveManager::FileDescription *ArchiveManager::get_file_by_seqno(ShardIdFull shard, BlockSeqno seqno,
bool key_block) {
auto &f = (key_block ? key_files_ : files_);
for (auto it = f.rbegin(); it != f.rend(); it++) {
auto i = it->second.first_blocks.find(shard);
if (i != it->second.first_blocks.end() && i->second.seqno <= seqno) {
return &it->second;
}
}
return nullptr;
}
void ArchiveManager::start_up() {
td::mkdir(db_root_).ensure();
td::mkdir(db_root_ + "/packed").ensure();
index_ = std::make_shared<td::RocksDb>(td::RocksDb::open(db_root_ + "/packed/globalindex").move_as_ok());
std::string value;
auto v = index_->get(create_serialize_tl_object<ton_api::db_archive_index_key>().as_slice(), value);
v.ensure();
if (v.move_as_ok() == td::KeyValue::GetStatus::Ok) {
auto R = fetch_tl_object<ton_api::db_archive_index_value>(value, true);
R.ensure();
auto x = R.move_as_ok();
for (auto &d : x->packages_) {
load_package(d, false);
}
for (auto &d : x->key_packages_) {
load_package(d, true);
}
}
}
} // namespace validator
} // namespace ton

View file

@ -0,0 +1,97 @@
#pragma once
#include "td/actor/actor.h"
#include "td/utils/buffer.h"
#include "ton/ton-types.h"
#include "td/utils/port/FileFd.h"
#include "package.hpp"
#include "filedb.hpp"
#include "validator/interfaces/block-handle.h"
#include <map>
#include <list>
namespace ton {
namespace validator {
class PackageWriter : public td::actor::Actor {
public:
PackageWriter(std::shared_ptr<Package> package) : package_(std::move(package)) {
}
void append(std::string filename, td::BufferSlice data, td::Promise<std::pair<td::uint64, td::uint64>> promise);
private:
std::shared_ptr<Package> package_;
};
class ArchiveFile : public td::actor::Actor {
public:
ArchiveFile(std::string path, UnixTime ts) : path_(std::move(path)), ts_(ts) {
}
void start_up() override;
void write(FileDb::RefId ref_id, td::BufferSlice data, td::Promise<td::Unit> promise);
void write_handle(BlockHandle handle, td::Promise<td::Unit> promise);
void read(FileDb::RefId ref_id, td::Promise<td::BufferSlice> promise);
void read_handle(BlockIdExt block_id, td::Promise<BlockHandle> promise);
private:
void completed_write(FileDb::RefId ref_id, td::uint64 offset, td::uint64 new_size, td::Promise<td::Unit> promise);
std::shared_ptr<Package> package_;
std::shared_ptr<td::KeyValue> index_;
td::actor::ActorOwn<PackageWriter> writer_;
std::string path_;
UnixTime ts_;
};
class ArchiveManager : public td::actor::Actor {
public:
ArchiveManager(std::string db_root);
void write(UnixTime ts, bool key_block, FileDb::RefId ref_id, td::BufferSlice data, td::Promise<td::Unit> promise);
void write_handle(BlockHandle handle, td::Promise<td::Unit> promise);
void read(UnixTime ts, bool key_block, FileDb::RefId ref_id, td::Promise<td::BufferSlice> promise);
void read_handle(BlockIdExt block_id, td::Promise<BlockHandle> promise);
void start_up() override;
private:
void read_handle_cont(BlockIdExt block_id, td::Promise<BlockHandle> promise);
struct FileDescription {
struct Desc {
BlockSeqno seqno;
LogicalTime lt;
};
FileDescription(UnixTime unix_time, bool key_block) : unix_time(unix_time), key_block(key_block) {
}
auto file_actor_id() const {
return file.get();
}
UnixTime unix_time;
bool key_block;
std::map<ShardIdFull, Desc> first_blocks;
td::actor::ActorOwn<ArchiveFile> file;
};
void load_package(UnixTime ts, bool key_block);
UnixTime convert_ts(UnixTime ts, bool key_block);
FileDescription *get_file(UnixTime ts, bool key_block, bool force = true);
FileDescription *add_file(UnixTime ts, bool key_block);
void update_desc(FileDescription &desc, ShardIdFull shard, BlockSeqno seqno, LogicalTime lt);
FileDescription *get_file_by_seqno(ShardIdFull shard, BlockSeqno seqno, bool key_block);
std::string db_root_;
std::map<UnixTime, FileDescription> files_;
std::map<UnixTime, FileDescription> key_files_;
std::shared_ptr<td::KeyValue> index_;
};
} // namespace validator
} // namespace ton

View file

@ -26,8 +26,13 @@ namespace validator {
BlockArchiver::BlockArchiver(BlockIdExt block_id, td::actor::ActorId<RootDb> root_db, BlockArchiver::BlockArchiver(BlockIdExt block_id, td::actor::ActorId<RootDb> root_db,
td::actor::ActorId<FileDb> file_db, td::actor::ActorId<FileDb> archive_db, td::actor::ActorId<FileDb> file_db, td::actor::ActorId<FileDb> archive_db,
td::Promise<td::Unit> promise) td::actor::ActorId<ArchiveManager> archive, td::Promise<td::Unit> promise)
: block_id_(block_id), root_db_(root_db), file_db_(file_db), archive_db_(archive_db), promise_(std::move(promise)) { : block_id_(block_id)
, root_db_(root_db)
, file_db_(file_db)
, archive_db_(archive_db)
, archive_(archive)
, promise_(std::move(promise)) {
} }
void BlockArchiver::start_up() { void BlockArchiver::start_up() {
@ -40,7 +45,7 @@ void BlockArchiver::start_up() {
void BlockArchiver::got_block_handle(BlockHandle handle) { void BlockArchiver::got_block_handle(BlockHandle handle) {
handle_ = std::move(handle); handle_ = std::move(handle);
if (handle_->moved_to_storage()) { if (handle_->moved_to_archive()) {
finish_query(); finish_query();
return; return;
} }
@ -63,16 +68,21 @@ void BlockArchiver::got_block_handle(BlockHandle handle) {
R.ensure(); R.ensure();
td::actor::send_closure(SelfId, &BlockArchiver::got_proof, R.move_as_ok()); td::actor::send_closure(SelfId, &BlockArchiver::got_proof, R.move_as_ok());
}); });
td::actor::send_closure(file_db_, &FileDb::load_file, FileDb::RefId{fileref::Proof{block_id_}}, std::move(P));
if (handle_->moved_to_storage()) {
td::actor::send_closure(archive_db_, &FileDb::load_file, FileDb::RefId{fileref::Proof{block_id_}}, std::move(P));
} else {
td::actor::send_closure(file_db_, &FileDb::load_file, FileDb::RefId{fileref::Proof{block_id_}}, std::move(P));
}
} }
void BlockArchiver::got_proof(td::BufferSlice data) { void BlockArchiver::got_proof(td::BufferSlice data) {
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<FileHash> R) { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Unit> R) {
R.ensure(); R.ensure();
td::actor::send_closure(SelfId, &BlockArchiver::written_proof); td::actor::send_closure(SelfId, &BlockArchiver::written_proof);
}); });
td::actor::send_closure(archive_db_, &FileDb::store_file, FileDb::RefId{fileref::Proof{block_id_}}, std::move(data), td::actor::send_closure(archive_, &ArchiveManager::write, handle_->unix_time(), handle_->is_key_block(),
std::move(P)); FileDb::RefId{fileref::Proof{block_id_}}, std::move(data), std::move(P));
} }
void BlockArchiver::written_proof() { void BlockArchiver::written_proof() {
@ -85,16 +95,21 @@ void BlockArchiver::written_proof() {
R.ensure(); R.ensure();
td::actor::send_closure(SelfId, &BlockArchiver::got_proof_link, R.move_as_ok()); td::actor::send_closure(SelfId, &BlockArchiver::got_proof_link, R.move_as_ok());
}); });
td::actor::send_closure(file_db_, &FileDb::load_file, FileDb::RefId{fileref::ProofLink{block_id_}}, std::move(P)); if (handle_->moved_to_storage()) {
td::actor::send_closure(archive_db_, &FileDb::load_file, FileDb::RefId{fileref::ProofLink{block_id_}},
std::move(P));
} else {
td::actor::send_closure(file_db_, &FileDb::load_file, FileDb::RefId{fileref::ProofLink{block_id_}}, std::move(P));
}
} }
void BlockArchiver::got_proof_link(td::BufferSlice data) { void BlockArchiver::got_proof_link(td::BufferSlice data) {
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<FileHash> R) { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Unit> R) {
R.ensure(); R.ensure();
td::actor::send_closure(SelfId, &BlockArchiver::written_proof_link); td::actor::send_closure(SelfId, &BlockArchiver::written_proof_link);
}); });
td::actor::send_closure(archive_db_, &FileDb::store_file, FileDb::RefId{fileref::ProofLink{block_id_}}, td::actor::send_closure(archive_, &ArchiveManager::write, handle_->unix_time(), handle_->is_key_block(),
std::move(data), std::move(P)); FileDb::RefId{fileref::ProofLink{block_id_}}, std::move(data), std::move(P));
} }
void BlockArchiver::written_proof_link() { void BlockArchiver::written_proof_link() {
@ -106,20 +121,24 @@ void BlockArchiver::written_proof_link() {
R.ensure(); R.ensure();
td::actor::send_closure(SelfId, &BlockArchiver::got_block_data, R.move_as_ok()); td::actor::send_closure(SelfId, &BlockArchiver::got_block_data, R.move_as_ok());
}); });
td::actor::send_closure(file_db_, &FileDb::load_file, FileDb::RefId{fileref::Block{block_id_}}, std::move(P)); if (handle_->moved_to_storage()) {
td::actor::send_closure(archive_db_, &FileDb::load_file, FileDb::RefId{fileref::Block{block_id_}}, std::move(P));
} else {
td::actor::send_closure(file_db_, &FileDb::load_file, FileDb::RefId{fileref::Block{block_id_}}, std::move(P));
}
} }
void BlockArchiver::got_block_data(td::BufferSlice data) { void BlockArchiver::got_block_data(td::BufferSlice data) {
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<FileHash> R) { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Unit> R) {
R.ensure(); R.ensure();
td::actor::send_closure(SelfId, &BlockArchiver::written_block_data); td::actor::send_closure(SelfId, &BlockArchiver::written_block_data);
}); });
td::actor::send_closure(archive_db_, &FileDb::store_file, FileDb::RefId{fileref::Block{block_id_}}, std::move(data), td::actor::send_closure(archive_, &ArchiveManager::write, handle_->unix_time(), handle_->is_key_block(),
std::move(P)); FileDb::RefId{fileref::Block{block_id_}}, std::move(data), std::move(P));
} }
void BlockArchiver::written_block_data() { void BlockArchiver::written_block_data() {
handle_->set_moved_to_storage(); handle_->set_moved_to_archive();
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Unit> R) { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Unit> R) {
R.ensure(); R.ensure();

View file

@ -22,6 +22,7 @@
#include "td/actor/actor.h" #include "td/actor/actor.h"
#include "validator/interfaces/block-handle.h" #include "validator/interfaces/block-handle.h"
#include "ton/ton-io.hpp" #include "ton/ton-io.hpp"
#include "archive-db.hpp"
namespace ton { namespace ton {
@ -33,7 +34,8 @@ class FileDb;
class BlockArchiver : public td::actor::Actor { class BlockArchiver : public td::actor::Actor {
public: public:
BlockArchiver(BlockIdExt block_id, td::actor::ActorId<RootDb> root_db, td::actor::ActorId<FileDb> file_db, BlockArchiver(BlockIdExt block_id, td::actor::ActorId<RootDb> root_db, td::actor::ActorId<FileDb> file_db,
td::actor::ActorId<FileDb> archive_db, td::Promise<td::Unit> promise); td::actor::ActorId<FileDb> archive_db, td::actor::ActorId<ArchiveManager> archive,
td::Promise<td::Unit> promise);
void abort_query(td::Status error); void abort_query(td::Status error);
@ -52,6 +54,7 @@ class BlockArchiver : public td::actor::Actor {
td::actor::ActorId<RootDb> root_db_; td::actor::ActorId<RootDb> root_db_;
td::actor::ActorId<FileDb> file_db_; td::actor::ActorId<FileDb> file_db_;
td::actor::ActorId<FileDb> archive_db_; td::actor::ActorId<FileDb> archive_db_;
td::actor::ActorId<ArchiveManager> archive_;
td::Promise<td::Unit> promise_; td::Promise<td::Unit> promise_;
BlockHandle handle_; BlockHandle handle_;

View file

@ -331,6 +331,9 @@ FileDb::RefId FileDb::get_ref_from_tl(const ton_api::db_filedb_Key& from) {
[&](const ton_api::db_filedb_key_candidate& key) { [&](const ton_api::db_filedb_key_candidate& key) {
ref_id = fileref::Candidate{PublicKey{key.id_->source_}, create_block_id(key.id_->id_), ref_id = fileref::Candidate{PublicKey{key.id_->source_}, create_block_id(key.id_->id_),
key.id_->collated_data_file_hash_}; key.id_->collated_data_file_hash_};
},
[&](const ton_api::db_filedb_key_blockInfo& key) {
ref_id = fileref::BlockInfo{create_block_id(key.block_id_)};
})); }));
return ref_id; return ref_id;
} }

View file

@ -130,6 +130,18 @@ class Candidate {
BlockIdExt block_id; BlockIdExt block_id;
FileHash collated_data_file_hash; FileHash collated_data_file_hash;
}; };
class BlockInfo {
public:
tl_object_ptr<ton_api::db_filedb_Key> tl() const {
return create_tl_object<ton_api::db_filedb_key_blockInfo>(create_tl_block_id(block_id));
}
FileHash hash() const {
return create_hash_tl_object<ton_api::db_filedb_key_blockInfo>(create_tl_block_id(block_id));
}
BlockIdExt block_id;
};
}; // namespace fileref }; // namespace fileref
class RootDb; class RootDb;
@ -138,7 +150,7 @@ class FileDb : public td::actor::Actor {
public: public:
using RefId = using RefId =
td::Variant<fileref::Empty, fileref::Block, fileref::ZeroState, fileref::PersistentState, fileref::Proof, td::Variant<fileref::Empty, fileref::Block, fileref::ZeroState, fileref::PersistentState, fileref::Proof,
fileref::Proof, fileref::ProofLink, fileref::Signatures, fileref::Candidate>; fileref::Proof, fileref::ProofLink, fileref::Signatures, fileref::Candidate, fileref::BlockInfo>;
using RefIdHash = td::Bits256; using RefIdHash = td::Bits256;
void store_file(RefId ref_id, td::BufferSlice data, td::Promise<FileHash> promise); void store_file(RefId ref_id, td::BufferSlice data, td::Promise<FileHash> promise);

View file

@ -52,7 +52,7 @@ class WriteFile : public td::actor::Actor {
auto res = R.move_as_ok(); auto res = R.move_as_ok();
auto file = std::move(res.first); auto file = std::move(res.first);
auto old_name = res.second; auto old_name = res.second;
size_t offset = 0; td::uint64 offset = 0;
while (data_.size() > 0) { while (data_.size() > 0) {
auto R = file.pwrite(data_.as_slice(), offset); auto R = file.pwrite(data_.as_slice(), offset);
auto s = R.move_as_ok(); auto s = R.move_as_ok();

143
validator/db/package.cpp Normal file
View file

@ -0,0 +1,143 @@
#include "package.hpp"
#include "common/errorcode.h"
namespace ton {
namespace {
constexpr td::uint32 header_size() {
return 4;
}
constexpr td::uint32 max_data_size() {
return (1u << 31) - 1;
}
constexpr td::uint32 max_filename_size() {
return (1u << 16) - 1;
}
constexpr td::uint16 entry_header_magic() {
return 0x1e8b;
}
constexpr td::uint32 package_header_magic() {
return 0xae8fdd01;
}
} // namespace
Package::Package(td::FileFd fd) : fd_(std::move(fd)) {
}
td::Status Package::truncate(td::uint64 size) {
TRY_STATUS(fd_.seek(size + header_size()));
return fd_.truncate_to_current_position(size + header_size());
}
td::uint64 Package::append(std::string filename, td::Slice data) {
CHECK(data.size() <= max_data_size());
CHECK(filename.size() <= max_filename_size());
auto size = fd_.get_size().move_as_ok();
auto orig_size = size;
td::uint32 header[2];
header[0] = entry_header_magic() + (td::narrow_cast<td::uint32>(filename.size()) << 16);
header[1] = td::narrow_cast<td::uint32>(data.size());
CHECK(fd_.pwrite(td::Slice(reinterpret_cast<const td::uint8*>(header), 8), size).move_as_ok() == 8);
size += 8;
CHECK(fd_.pwrite(filename, size).move_as_ok() == filename.size());
size += filename.size();
CHECK(fd_.pwrite(data, size).move_as_ok() == data.size());
size += data.size();
fd_.sync().ensure();
return orig_size - header_size();
}
td::uint64 Package::size() const {
return fd_.get_size().move_as_ok() - header_size();
}
td::Result<std::pair<std::string, td::BufferSlice>> Package::read(td::uint64 offset) const {
offset += header_size();
td::uint32 header[2];
TRY_RESULT(s1, fd_.pread(td::MutableSlice(reinterpret_cast<td::uint8*>(header), 8), offset));
if (s1 != 8) {
return td::Status::Error(ErrorCode::notready, "too short read");
}
if ((header[0] & 0xffff) != entry_header_magic()) {
return td::Status::Error(ErrorCode::notready, "bad entry magic");
}
offset += 8;
auto fname_size = header[0] >> 16;
auto data_size = header[1];
std::string fname(fname_size, '\0');
TRY_RESULT(s2, fd_.pread(fname, offset));
if (s2 != fname_size) {
return td::Status::Error(ErrorCode::notready, "too short read (filename)");
}
offset += fname_size;
td::BufferSlice data{data_size};
TRY_RESULT(s3, fd_.pread(data.as_slice(), offset));
if (s3 != data_size) {
return td::Status::Error(ErrorCode::notready, "too short read (data)");
}
return std::pair<std::string, td::BufferSlice>{std::move(fname), std::move(data)};
}
td::Result<td::uint64> Package::advance(td::uint64 offset) {
offset += header_size();
td::uint32 header[2];
TRY_RESULT(s1, fd_.pread(td::MutableSlice(reinterpret_cast<td::uint8*>(header), 8), offset));
if (s1 != 8) {
return td::Status::Error(ErrorCode::notready, "too short read");
}
if ((header[0] & 0xffff) != entry_header_magic()) {
return td::Status::Error(ErrorCode::notready, "bad entry magic");
}
offset += 8 + (header[0] >> 16) + header[1];
if (offset > static_cast<td::uint64>(fd_.get_size().move_as_ok())) {
return td::Status::Error(ErrorCode::notready, "truncated read");
}
return offset - header_size();
}
td::Result<Package> Package::open(std::string path, bool read_only, bool create) {
td::uint32 flags = td::FileFd::Flags::Read;
if (!read_only) {
flags |= td::FileFd::Write;
}
if (create) {
flags |= td::FileFd::Create;
}
TRY_RESULT(fd, td::FileFd::open(path, flags));
TRY_RESULT(size, fd.get_size());
if (size < header_size()) {
if (!create) {
return td::Status::Error(ErrorCode::notready, "db is too short");
}
td::uint32 header[1];
header[0] = package_header_magic();
TRY_RESULT(s, fd.pwrite(td::Slice(reinterpret_cast<const td::uint8*>(header), header_size()), size));
if (s != header_size()) {
return td::Status::Error(ErrorCode::notready, "db write is short");
}
} else {
td::uint32 header[1];
TRY_RESULT(s, fd.pread(td::MutableSlice(reinterpret_cast<td::uint8*>(header), header_size()), 0));
if (s != header_size()) {
return td::Status::Error(ErrorCode::notready, "db read failed");
}
if (header[0] != package_header_magic()) {
return td::Status::Error(ErrorCode::notready, "magic mismatch");
}
}
return Package{std::move(fd)};
}
} // namespace ton

40
validator/db/package.hpp Normal file
View file

@ -0,0 +1,40 @@
#pragma once
#include "td/actor/actor.h"
#include "td/utils/port/FileFd.h"
#include "td/utils/buffer.h"
namespace ton {
class Package {
public:
static td::Result<Package> open(std::string path, bool read_only = false, bool create = false);
Package(td::FileFd fd);
td::Status truncate(td::uint64 size);
td::uint64 append(std::string filename, td::Slice data);
td::uint64 size() const;
td::Result<std::pair<std::string, td::BufferSlice>> read(td::uint64 offset) const;
td::Result<td::uint64> advance(td::uint64 offset);
struct Iterator {
td::uint64 offset;
Package &package;
Iterator operator++(int);
const Iterator operator++(int) const;
td::Result<std::pair<std::string, td::BufferSlice>> read() const;
};
Iterator begin();
const Iterator begin() const;
Iterator end();
const Iterator end() const;
private:
td::FileFd fd_;
};
} // namespace ton

View file

@ -32,7 +32,7 @@ namespace ton {
namespace validator { namespace validator {
void RootDb::store_block_data(BlockHandle handle, td::Ref<BlockData> block, td::Promise<td::Unit> promise) { void RootDb::store_block_data(BlockHandle handle, td::Ref<BlockData> block, td::Promise<td::Unit> promise) {
if (handle->moved_to_storage()) { if (handle->moved_to_storage() || handle->moved_to_archive()) {
promise.set_value(td::Unit()); promise.set_value(td::Unit());
return; return;
} }
@ -64,14 +64,19 @@ void RootDb::get_block_data(BlockHandle handle, td::Promise<td::Ref<BlockData>>
} }
}); });
td::actor::send_closure(handle->moved_to_storage() ? archive_db_.get() : file_db_.get(), &FileDb::load_file, if (handle->moved_to_archive()) {
FileDb::RefId{fileref::Block{handle->id()}}, std::move(P)); td::actor::send_closure(new_archive_db_, &ArchiveManager::read, handle->unix_time(), handle->is_key_block(),
FileDb::RefId{fileref::Block{handle->id()}}, std::move(P));
} else {
td::actor::send_closure(handle->moved_to_storage() ? old_archive_db_.get() : file_db_.get(), &FileDb::load_file,
FileDb::RefId{fileref::Block{handle->id()}}, std::move(P));
}
} }
} }
void RootDb::store_block_signatures(BlockHandle handle, td::Ref<BlockSignatureSet> data, void RootDb::store_block_signatures(BlockHandle handle, td::Ref<BlockSignatureSet> data,
td::Promise<td::Unit> promise) { td::Promise<td::Unit> promise) {
if (handle->moved_to_storage()) { if (handle->moved_to_storage() || handle->moved_to_archive()) {
promise.set_value(td::Unit()); promise.set_value(td::Unit());
return; return;
} }
@ -94,7 +99,7 @@ void RootDb::get_block_signatures(BlockHandle handle, td::Promise<td::Ref<BlockS
if (!handle->inited_signatures()) { if (!handle->inited_signatures()) {
promise.set_error(td::Status::Error(ErrorCode::notready, "not in db")); promise.set_error(td::Status::Error(ErrorCode::notready, "not in db"));
} else { } else {
if (handle->moved_to_storage()) { if (handle->moved_to_storage() || handle->moved_to_archive()) {
promise.set_error(td::Status::Error(ErrorCode::error, "signatures already gc'd")); promise.set_error(td::Status::Error(ErrorCode::error, "signatures already gc'd"));
return; return;
} }
@ -111,7 +116,7 @@ void RootDb::get_block_signatures(BlockHandle handle, td::Promise<td::Ref<BlockS
} }
void RootDb::store_block_proof(BlockHandle handle, td::Ref<Proof> proof, td::Promise<td::Unit> promise) { void RootDb::store_block_proof(BlockHandle handle, td::Ref<Proof> proof, td::Promise<td::Unit> promise) {
if (handle->moved_to_storage()) { if (handle->moved_to_storage() || handle->moved_to_archive()) {
promise.set_value(td::Unit()); promise.set_value(td::Unit());
return; return;
} }
@ -142,13 +147,18 @@ void RootDb::get_block_proof(BlockHandle handle, td::Promise<td::Ref<Proof>> pro
promise.set_result(create_proof(id, R.move_as_ok())); promise.set_result(create_proof(id, R.move_as_ok()));
} }
}); });
td::actor::send_closure(handle->moved_to_storage() ? archive_db_.get() : file_db_.get(), &FileDb::load_file, if (handle->moved_to_archive()) {
FileDb::RefId{fileref::Proof{handle->id()}}, std::move(P)); td::actor::send_closure(new_archive_db_, &ArchiveManager::read, handle->unix_time(), handle->is_key_block(),
FileDb::RefId{fileref::Proof{handle->id()}}, std::move(P));
} else {
td::actor::send_closure(handle->moved_to_storage() ? old_archive_db_.get() : file_db_.get(), &FileDb::load_file,
FileDb::RefId{fileref::Proof{handle->id()}}, std::move(P));
}
} }
} }
void RootDb::store_block_proof_link(BlockHandle handle, td::Ref<ProofLink> proof, td::Promise<td::Unit> promise) { void RootDb::store_block_proof_link(BlockHandle handle, td::Ref<ProofLink> proof, td::Promise<td::Unit> promise) {
if (handle->moved_to_storage()) { if (handle->moved_to_storage() || handle->moved_to_archive()) {
promise.set_value(td::Unit()); promise.set_value(td::Unit());
return; return;
} }
@ -179,8 +189,13 @@ void RootDb::get_block_proof_link(BlockHandle handle, td::Promise<td::Ref<ProofL
promise.set_result(create_proof_link(id, R.move_as_ok())); promise.set_result(create_proof_link(id, R.move_as_ok()));
} }
}); });
td::actor::send_closure(handle->moved_to_storage() ? archive_db_.get() : file_db_.get(), &FileDb::load_file, if (handle->moved_to_archive()) {
FileDb::RefId{fileref::ProofLink{handle->id()}}, std::move(P)); td::actor::send_closure(new_archive_db_, &ArchiveManager::read, handle->unix_time(), handle->is_key_block(),
FileDb::RefId{fileref::ProofLink{handle->id()}}, std::move(P));
} else {
td::actor::send_closure(handle->moved_to_storage() ? old_archive_db_.get() : file_db_.get(), &FileDb::load_file,
FileDb::RefId{fileref::ProofLink{handle->id()}}, std::move(P));
}
} }
} }
@ -225,7 +240,7 @@ void RootDb::get_block_candidate(PublicKey source, BlockIdExt id, FileHash colla
void RootDb::store_block_state(BlockHandle handle, td::Ref<ShardState> state, void RootDb::store_block_state(BlockHandle handle, td::Ref<ShardState> state,
td::Promise<td::Ref<ShardState>> promise) { td::Promise<td::Ref<ShardState>> promise) {
if (handle->moved_to_storage()) { if (handle->moved_to_storage() || handle->moved_to_archive()) {
promise.set_value(std::move(state)); promise.set_value(std::move(state));
return; return;
} }
@ -290,27 +305,27 @@ void RootDb::store_persistent_state_file(BlockIdExt block_id, BlockIdExt masterc
} }
}); });
td::actor::send_closure(archive_db_, &FileDb::store_file, td::actor::send_closure(old_archive_db_, &FileDb::store_file,
FileDb::RefId{fileref::PersistentState{block_id, masterchain_block_id}}, std::move(state), FileDb::RefId{fileref::PersistentState{block_id, masterchain_block_id}}, std::move(state),
std::move(P)); std::move(P));
} }
void RootDb::get_persistent_state_file(BlockIdExt block_id, BlockIdExt masterchain_block_id, void RootDb::get_persistent_state_file(BlockIdExt block_id, BlockIdExt masterchain_block_id,
td::Promise<td::BufferSlice> promise) { td::Promise<td::BufferSlice> promise) {
td::actor::send_closure(archive_db_, &FileDb::load_file, td::actor::send_closure(old_archive_db_, &FileDb::load_file,
FileDb::RefId{fileref::PersistentState{block_id, masterchain_block_id}}, std::move(promise)); FileDb::RefId{fileref::PersistentState{block_id, masterchain_block_id}}, std::move(promise));
} }
void RootDb::get_persistent_state_file_slice(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::int64 offset, void RootDb::get_persistent_state_file_slice(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::int64 offset,
td::int64 max_size, td::Promise<td::BufferSlice> promise) { td::int64 max_size, td::Promise<td::BufferSlice> promise) {
td::actor::send_closure(archive_db_, &FileDb::load_file_slice, td::actor::send_closure(old_archive_db_, &FileDb::load_file_slice,
FileDb::RefId{fileref::PersistentState{block_id, masterchain_block_id}}, offset, max_size, FileDb::RefId{fileref::PersistentState{block_id, masterchain_block_id}}, offset, max_size,
std::move(promise)); std::move(promise));
} }
void RootDb::check_persistent_state_file_exists(BlockIdExt block_id, BlockIdExt masterchain_block_id, void RootDb::check_persistent_state_file_exists(BlockIdExt block_id, BlockIdExt masterchain_block_id,
td::Promise<bool> promise) { td::Promise<bool> promise) {
td::actor::send_closure(archive_db_, &FileDb::check_file, td::actor::send_closure(old_archive_db_, &FileDb::check_file,
FileDb::RefId{fileref::PersistentState{block_id, masterchain_block_id}}, std::move(promise)); FileDb::RefId{fileref::PersistentState{block_id, masterchain_block_id}}, std::move(promise));
} }
@ -325,26 +340,38 @@ void RootDb::store_zero_state_file(BlockIdExt block_id, td::BufferSlice state, t
} }
}); });
td::actor::send_closure(archive_db_, &FileDb::store_file, FileDb::RefId{fileref::ZeroState{block_id}}, td::actor::send_closure(old_archive_db_, &FileDb::store_file, FileDb::RefId{fileref::ZeroState{block_id}},
std::move(state), std::move(P)); std::move(state), std::move(P));
} }
void RootDb::get_zero_state_file(BlockIdExt block_id, td::Promise<td::BufferSlice> promise) { void RootDb::get_zero_state_file(BlockIdExt block_id, td::Promise<td::BufferSlice> promise) {
td::actor::send_closure(archive_db_, &FileDb::load_file, FileDb::RefId{fileref::ZeroState{block_id}}, td::actor::send_closure(old_archive_db_, &FileDb::load_file, FileDb::RefId{fileref::ZeroState{block_id}},
std::move(promise)); std::move(promise));
} }
void RootDb::check_zero_state_file_exists(BlockIdExt block_id, td::Promise<bool> promise) { void RootDb::check_zero_state_file_exists(BlockIdExt block_id, td::Promise<bool> promise) {
td::actor::send_closure(archive_db_, &FileDb::check_file, FileDb::RefId{fileref::ZeroState{block_id}}, td::actor::send_closure(old_archive_db_, &FileDb::check_file, FileDb::RefId{fileref::ZeroState{block_id}},
std::move(promise)); std::move(promise));
} }
void RootDb::store_block_handle(BlockHandle handle, td::Promise<td::Unit> promise) { void RootDb::store_block_handle(BlockHandle handle, td::Promise<td::Unit> promise) {
td::actor::send_closure(block_db_, &BlockDb::store_block_handle, std::move(handle), std::move(promise)); if (handle->moved_to_archive()) {
td::actor::send_closure(new_archive_db_, &ArchiveManager::write_handle, std::move(handle), std::move(promise));
} else {
td::actor::send_closure(block_db_, &BlockDb::store_block_handle, std::move(handle), std::move(promise));
}
} }
void RootDb::get_block_handle(BlockIdExt id, td::Promise<BlockHandle> promise) { void RootDb::get_block_handle(BlockIdExt id, td::Promise<BlockHandle> promise) {
td::actor::send_closure(block_db_, &BlockDb::get_block_handle, id, std::move(promise)); auto P = td::PromiseCreator::lambda(
[db = block_db_.get(), id, promise = std::move(promise)](td::Result<BlockHandle> R) mutable {
if (R.is_error()) {
td::actor::send_closure(db, &BlockDb::get_block_handle, id, std::move(promise));
} else {
promise.set_value(R.move_as_ok());
}
});
td::actor::send_closure(new_archive_db_, &ArchiveManager::read_handle, id, std::move(P));
} }
void RootDb::try_get_static_file(FileHash file_hash, td::Promise<td::BufferSlice> promise) { void RootDb::try_get_static_file(FileHash file_hash, td::Promise<td::BufferSlice> promise) {
@ -426,16 +453,17 @@ void RootDb::start_up() {
cell_db_ = td::actor::create_actor<CellDb>("celldb", actor_id(this), root_path_ + "/celldb/"); cell_db_ = td::actor::create_actor<CellDb>("celldb", actor_id(this), root_path_ + "/celldb/");
block_db_ = td::actor::create_actor<BlockDb>("blockdb", actor_id(this), root_path_ + "/blockdb/"); block_db_ = td::actor::create_actor<BlockDb>("blockdb", actor_id(this), root_path_ + "/blockdb/");
file_db_ = td::actor::create_actor<FileDb>("filedb", actor_id(this), root_path_ + "/files/", depth_, false); file_db_ = td::actor::create_actor<FileDb>("filedb", actor_id(this), root_path_ + "/files/", depth_, false);
archive_db_ = old_archive_db_ =
td::actor::create_actor<FileDb>("filedbarchive", actor_id(this), root_path_ + "/archive/", depth_, true); td::actor::create_actor<FileDb>("filedbarchive", actor_id(this), root_path_ + "/archive/", depth_, true);
lt_db_ = td::actor::create_actor<LtDb>("ltdb", actor_id(this), root_path_ + "/ltdb/"); lt_db_ = td::actor::create_actor<LtDb>("ltdb", actor_id(this), root_path_ + "/ltdb/");
state_db_ = td::actor::create_actor<StateDb>("statedb", actor_id(this), root_path_ + "/state/"); state_db_ = td::actor::create_actor<StateDb>("statedb", actor_id(this), root_path_ + "/state/");
static_files_db_ = td::actor::create_actor<StaticFilesDb>("staticfilesdb", actor_id(this), root_path_ + "/static/"); static_files_db_ = td::actor::create_actor<StaticFilesDb>("staticfilesdb", actor_id(this), root_path_ + "/static/");
new_archive_db_ = td::actor::create_actor<ArchiveManager>("archivemanager", root_path_ + "/archive/");
} }
void RootDb::archive(BlockIdExt block_id, td::Promise<td::Unit> promise) { void RootDb::archive(BlockIdExt block_id, td::Promise<td::Unit> promise) {
td::actor::create_actor<BlockArchiver>("archiveblock", block_id, actor_id(this), file_db_.get(), archive_db_.get(), td::actor::create_actor<BlockArchiver>("archiveblock", block_id, actor_id(this), file_db_.get(),
std::move(promise)) old_archive_db_.get(), new_archive_db_.get(), std::move(promise))
.release(); .release();
} }
@ -480,14 +508,15 @@ void RootDb::allow_gc(FileDb::RefId ref_id, bool is_archive, td::Promise<bool> p
CHECK(!is_archive); CHECK(!is_archive);
td::actor::send_closure(validator_manager_, &ValidatorManager::allow_block_candidate_gc, td::actor::send_closure(validator_manager_, &ValidatorManager::allow_block_candidate_gc,
key.block_id, std::move(promise)); key.block_id, std::move(promise));
})); },
[&](const fileref::BlockInfo &key) { UNREACHABLE(); }));
} }
void RootDb::prepare_stats(td::Promise<std::vector<std::pair<std::string, std::string>>> promise) { void RootDb::prepare_stats(td::Promise<std::vector<std::pair<std::string, std::string>>> promise) {
auto merger = StatsMerger::create(std::move(promise)); auto merger = StatsMerger::create(std::move(promise));
td::actor::send_closure(file_db_, &FileDb::prepare_stats, merger.make_promise("filedb.")); td::actor::send_closure(file_db_, &FileDb::prepare_stats, merger.make_promise("filedb."));
td::actor::send_closure(archive_db_, &FileDb::prepare_stats, merger.make_promise("archivedb.")); td::actor::send_closure(old_archive_db_, &FileDb::prepare_stats, merger.make_promise("archivedb."));
} }
void RootDb::truncate(td::Ref<MasterchainState> state, td::Promise<td::Unit> promise) { void RootDb::truncate(td::Ref<MasterchainState> state, td::Promise<td::Unit> promise) {

View file

@ -28,6 +28,7 @@
#include "ltdb.hpp" #include "ltdb.hpp"
#include "statedb.hpp" #include "statedb.hpp"
#include "staticfilesdb.hpp" #include "staticfilesdb.hpp"
#include "archive-db.hpp"
namespace ton { namespace ton {
@ -126,10 +127,11 @@ class RootDb : public Db {
td::actor::ActorOwn<CellDb> cell_db_; td::actor::ActorOwn<CellDb> cell_db_;
td::actor::ActorOwn<BlockDb> block_db_; td::actor::ActorOwn<BlockDb> block_db_;
td::actor::ActorOwn<FileDb> file_db_; td::actor::ActorOwn<FileDb> file_db_;
td::actor::ActorOwn<FileDb> archive_db_; td::actor::ActorOwn<FileDb> old_archive_db_;
td::actor::ActorOwn<LtDb> lt_db_; td::actor::ActorOwn<LtDb> lt_db_;
td::actor::ActorOwn<StateDb> state_db_; td::actor::ActorOwn<StateDb> state_db_;
td::actor::ActorOwn<StaticFilesDb> static_files_db_; td::actor::ActorOwn<StaticFilesDb> static_files_db_;
td::actor::ActorOwn<ArchiveManager> new_archive_db_;
}; };
} // namespace validator } // namespace validator

Some files were not shown because too many files have changed in this diff Show more