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

updated submodules, bugfixes

- added new fift/func code for validator complaint creation
- bugfixes in validator
- updates in tonlib
- new versions of rocksdb/abseil
- hardfork support
This commit is contained in:
ton 2020-04-27 16:01:46 +04:00
parent 16a4566091
commit 9f008b129f
129 changed files with 8438 additions and 879 deletions

1
.gitignore vendored
View file

@ -7,7 +7,6 @@ tl/generate/auto/
compile_commands.json compile_commands.json
crypto/block/block-auto.cpp crypto/block/block-auto.cpp
crypto/block/block-auto.h crypto/block/block-auto.h
crypto/smartcont/*-code.fif
crypto/smartcont/auto/ crypto/smartcont/auto/
test/regression-tests.cache/ test/regression-tests.cache/
*.swp *.swp

View file

@ -115,6 +115,7 @@ if (TON_USE_ROCKSDB)
if (ANDROID) if (ANDROID)
set(PORTABLE ON CACHE BOOL "portable") set(PORTABLE ON CACHE BOOL "portable")
endif() endif()
set(WITH_GFLAGS OFF CACHE BOOL "build with GFlags")
set(WITH_TESTS OFF CACHE BOOL "build with tests") set(WITH_TESTS OFF CACHE BOOL "build with tests")
set(WITH_TOOLS OFF CACHE BOOL "build with tools") set(WITH_TOOLS OFF CACHE BOOL "build with tools")
set(FAIL_ON_WARNINGS OFF CACHE BOOL "fail on warnings") set(FAIL_ON_WARNINGS OFF CACHE BOOL "fail on warnings")
@ -231,7 +232,7 @@ elseif (CLANG OR GCC)
if (APPLE) if (APPLE)
#use "-Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/export_list" for exported symbols #use "-Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/export_list" for exported symbols
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fvisibility=hidden -Wl,-dead_strip,-x,-S") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fvisibility=hidden -Wl,-dead_strip,-x,-S")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fvisibility=hidden -Wl,-dead_strip,-x,-S") #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fvisibility=hidden -Wl,-dead_strip,-x,-S")
else() else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections -Wl,--exclude-libs,ALL") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections -Wl,--exclude-libs,ALL")
@ -385,6 +386,7 @@ add_subdirectory(validator)
add_subdirectory(blockchain-explorer) add_subdirectory(blockchain-explorer)
add_subdirectory(validator-engine) add_subdirectory(validator-engine)
add_subdirectory(validator-engine-console) add_subdirectory(validator-engine-console)
add_subdirectory(create-hardfork)
add_subdirectory(dht-server) add_subdirectory(dht-server)
add_subdirectory(utils) add_subdirectory(utils)
add_subdirectory(http) add_subdirectory(http)
@ -445,6 +447,9 @@ if (NOT TON_ONLY_TONLIB)
add_executable(test-db test/test-td-main.cpp ${TONDB_TEST_SOURCE}) add_executable(test-db test/test-td-main.cpp ${TONDB_TEST_SOURCE})
target_link_libraries(test-db PRIVATE ton_db memprof) target_link_libraries(test-db PRIVATE ton_db memprof)
add_executable(test-rocksdb test/test-rocksdb.cpp)
target_link_libraries(test-rocksdb PRIVATE memprof tddb tdutils)
add_executable(test-tddb test/test-td-main.cpp ${TDDB_TEST_SOURCE}) add_executable(test-tddb test/test-td-main.cpp ${TDDB_TEST_SOURCE})
target_link_libraries(test-tddb PRIVATE tdutils tddb ${CMAKE_THREAD_LIBS_INIT} memprof) target_link_libraries(test-tddb PRIVATE tdutils tddb ${CMAKE_THREAD_LIBS_INIT} memprof)

View file

@ -1505,7 +1505,7 @@ void HttpQueryStatus::finish_query() {
td::uint32 j = 0; td::uint32 j = 0;
for (auto &X : results_.results) { for (auto &X : results_.results) {
if (!X->values_[i].is_valid()) { if (!X->values_[i].is_valid()) {
A << "<td>FAIL</td>"; A << "<td class=\"table-danger\">FAIL</td>";
} else { } else {
if (m[j].count(X->values_[i].id.seqno) == 0) { if (m[j].count(X->values_[i].id.seqno) == 0) {
m[j].insert(X->values_[i].id.seqno); m[j].insert(X->values_[i].id.seqno);

View file

@ -49,6 +49,7 @@ set(TON_CRYPTO_SOURCE
common/bigexp.h common/bigexp.h
common/util.h common/util.h
common/linalloc.hpp common/linalloc.hpp
common/promiseop.hpp
ellcurve/Ed25519.h ellcurve/Ed25519.h
ellcurve/Fp25519.h ellcurve/Fp25519.h
@ -212,6 +213,7 @@ set(SMC_ENVELOPE_SOURCE
smc-envelope/HighloadWalletV2.cpp smc-envelope/HighloadWalletV2.cpp
smc-envelope/ManualDns.cpp smc-envelope/ManualDns.cpp
smc-envelope/MultisigWallet.cpp smc-envelope/MultisigWallet.cpp
smc-envelope/PaymentChannel.cpp
smc-envelope/SmartContract.cpp smc-envelope/SmartContract.cpp
smc-envelope/SmartContractCode.cpp smc-envelope/SmartContractCode.cpp
smc-envelope/TestGiver.cpp smc-envelope/TestGiver.cpp
@ -341,7 +343,7 @@ if (NOT CMAKE_CROSSCOMPILING)
set(multiValueArgs SOURCE) set(multiValueArgs SOURCE)
set(FUNC_LIB_SOURCE smartcont/stdlib.fc) set(FUNC_LIB_SOURCE smartcont/stdlib.fc)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
string(REGEX REPLACE "[^a-zA-Z_]" "_" ID ${ARG_DEST}) string(REGEX REPLACE "[^0-9a-zA-Z_]" "_" ID ${ARG_DEST})
set(ARG_DEST_FIF "${ARG_DEST}.fif") set(ARG_DEST_FIF "${ARG_DEST}.fif")
add_custom_command( add_custom_command(
COMMENT "Generate ${ARG_DEST_FIF}" COMMENT "Generate ${ARG_DEST_FIF}"
@ -374,10 +376,13 @@ if (NOT CMAKE_CROSSCOMPILING)
GenFif(DEST smartcont/auto/multisig-code SOURCE smartcont/multisig-code.fc NAME multisig) 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/restricted-wallet3-code SOURCE smartcont/restricted-wallet3-code.fc NAME restricted-wallet3)
GenFif(DEST smartcont/auto/dns-manual-code SOURCE smartcont/dns-manual-code.fc NAME dns-manual) GenFif(DEST smartcont/auto/dns-manual-code SOURCE smartcont/dns-manual-code.fc NAME dns-manual)
GenFif(DEST smartcont/auto/dns-auto-code SOURCE smartcont/dns-auto-code.fc NAME dns-auto) GenFif(DEST smartcont/auto/dns-auto-code SOURCE smartcont/dns-auto-code.fc NAME dns-auto)
GenFif(DEST smartcont/auto/payment-channel-code SOURCE smartcont/payment-channel-code.fc NAME payment-channel)
GenFif(DEST smartcont/auto/simple-wallet-ext-code SOURCE smartcont/simple-wallet-ext-code.fc NAME simple-wallet-ext) GenFif(DEST smartcont/auto/simple-wallet-ext-code SOURCE smartcont/simple-wallet-ext-code.fc NAME simple-wallet-ext)
endif() endif()
@ -420,5 +425,13 @@ if (WINGETOPT_FOUND)
target_link_libraries_system(dump-block wingetopt) target_link_libraries_system(dump-block wingetopt)
endif() endif()
add_executable(test-weight-distr block/test-weight-distr.cpp)
target_include_directories(test-weight-distr PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
target_link_libraries(test-weight-distr PUBLIC ton_crypto fift-lib ton_block)
if (WINGETOPT_FOUND)
target_link_libraries_system(test-weight-distr wingetopt)
endif()
install(TARGETS fift func RUNTIME DESTINATION bin) install(TARGETS fift func RUNTIME DESTINATION bin)
install(DIRECTORY fift/lib/ DESTINATION lib/fift) install(DIRECTORY fift/lib/ DESTINATION lib/fift)

View file

@ -241,6 +241,14 @@ bool MsgAddressInt::extract_std_address(vm::CellSlice& cs, ton::WorkchainId& wor
return false; return false;
} }
bool MsgAddressInt::extract_std_address(Ref<vm::CellSlice> cs_ref, block::StdAddress& addr, bool rewrite) const {
return extract_std_address(std::move(cs_ref), addr.workchain, addr.addr, rewrite);
}
bool MsgAddressInt::extract_std_address(vm::CellSlice& cs, block::StdAddress& addr, bool rewrite) const {
return extract_std_address(cs, addr.workchain, addr.addr, rewrite);
}
bool MsgAddressInt::store_std_address(vm::CellBuilder& cb, ton::WorkchainId workchain, bool MsgAddressInt::store_std_address(vm::CellBuilder& cb, ton::WorkchainId workchain,
const ton::StdSmcAddress& addr) const { const ton::StdSmcAddress& addr) const {
if (workchain >= -128 && workchain < 128) { if (workchain >= -128 && workchain < 128) {
@ -263,6 +271,14 @@ Ref<vm::CellSlice> MsgAddressInt::pack_std_address(ton::WorkchainId workchain, c
} }
} }
bool MsgAddressInt::store_std_address(vm::CellBuilder& cb, const block::StdAddress& addr) const {
return store_std_address(cb, addr.workchain, addr.addr);
}
Ref<vm::CellSlice> MsgAddressInt::pack_std_address(const block::StdAddress& addr) const {
return pack_std_address(addr.workchain, addr.addr);
}
const MsgAddressInt t_MsgAddressInt; const MsgAddressInt t_MsgAddressInt;
bool MsgAddress::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const { bool MsgAddress::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {

View file

@ -298,8 +298,13 @@ struct MsgAddressInt final : TLB_Complex {
bool rewrite = true) const; bool rewrite = true) const;
bool extract_std_address(vm::CellSlice& cs, ton::WorkchainId& workchain, ton::StdSmcAddress& addr, bool extract_std_address(vm::CellSlice& cs, ton::WorkchainId& workchain, ton::StdSmcAddress& addr,
bool rewrite = true) const; bool rewrite = true) const;
bool extract_std_address(Ref<vm::CellSlice> cs_ref, block::StdAddress& addr, bool rewrite = true) const;
bool extract_std_address(vm::CellSlice& cs, block::StdAddress& addr, bool rewrite = true) const;
bool store_std_address(vm::CellBuilder& cb, ton::WorkchainId workchain, const ton::StdSmcAddress& addr) const; bool store_std_address(vm::CellBuilder& cb, ton::WorkchainId workchain, const ton::StdSmcAddress& addr) const;
Ref<vm::CellSlice> pack_std_address(ton::WorkchainId workchain, const ton::StdSmcAddress& addr) const; Ref<vm::CellSlice> pack_std_address(ton::WorkchainId workchain, const ton::StdSmcAddress& addr) const;
bool store_std_address(vm::CellBuilder& cb, const block::StdAddress& addr) const;
Ref<vm::CellSlice> pack_std_address(const block::StdAddress& addr) const;
}; };
extern const MsgAddressInt t_MsgAddressInt; extern const MsgAddressInt t_MsgAddressInt;

View file

@ -27,6 +27,7 @@
#include "td/utils/crypto.h" #include "td/utils/crypto.h"
#include "td/utils/tl_storers.h" #include "td/utils/tl_storers.h"
#include "td/utils/misc.h" #include "td/utils/misc.h"
#include "td/utils/Random.h"
namespace block { namespace block {
using namespace std::literals::string_literals; using namespace std::literals::string_literals;
@ -369,14 +370,14 @@ std::unique_ptr<MsgProcessedUptoCollection> MsgProcessedUptoCollection::unpack(t
return v && v->valid ? std::move(v) : std::unique_ptr<MsgProcessedUptoCollection>{}; return v && v->valid ? std::move(v) : std::unique_ptr<MsgProcessedUptoCollection>{};
} }
bool MsgProcessedUpto::contains(const MsgProcessedUpto& other) const & { bool MsgProcessedUpto::contains(const MsgProcessedUpto& other) const& {
return ton::shard_is_ancestor(shard, other.shard) && mc_seqno >= other.mc_seqno && return ton::shard_is_ancestor(shard, other.shard) && mc_seqno >= other.mc_seqno &&
(last_inmsg_lt > other.last_inmsg_lt || (last_inmsg_lt > other.last_inmsg_lt ||
(last_inmsg_lt == other.last_inmsg_lt && !(last_inmsg_hash < other.last_inmsg_hash))); (last_inmsg_lt == other.last_inmsg_lt && !(last_inmsg_hash < other.last_inmsg_hash)));
} }
bool MsgProcessedUpto::contains(ton::ShardId other_shard, ton::LogicalTime other_lt, td::ConstBitPtr other_hash, bool MsgProcessedUpto::contains(ton::ShardId other_shard, ton::LogicalTime other_lt, td::ConstBitPtr other_hash,
ton::BlockSeqno other_mc_seqno) const & { ton::BlockSeqno other_mc_seqno) const& {
return ton::shard_is_ancestor(shard, other_shard) && mc_seqno >= other_mc_seqno && return ton::shard_is_ancestor(shard, other_shard) && mc_seqno >= other_mc_seqno &&
(last_inmsg_lt > other_lt || (last_inmsg_lt == other_lt && !(last_inmsg_hash < other_hash))); (last_inmsg_lt > other_lt || (last_inmsg_lt == other_lt && !(last_inmsg_hash < other_hash)));
} }
@ -1546,6 +1547,89 @@ bool unpack_CreatorStats(Ref<vm::CellSlice> cs, DiscountedCounter& mc_cnt, Disco
} }
} }
/*
*
* Monte Carlo simulator for computing the share of shardchain blocks generated by each validator
*
*/
bool MtCarloComputeShare::compute() {
ok = false;
if (W.size() >= (1U << 31) || W.empty()) {
return false;
}
K = std::min(K, N);
if (K <= 0 || iterations <= 0) {
return false;
}
double tot_weight = 0., acc = 0.;
for (int i = 0; i < N; i++) {
if (W[i] <= 0.) {
return false;
}
tot_weight += W[i];
}
CW.resize(N);
RW.resize(N);
for (int i = 0; i < N; i++) {
CW[i] = acc;
acc += W[i] /= tot_weight;
RW[i] = 0.;
}
R0 = 0.;
H.resize(N);
A.resize(K);
for (long long it = 0; it < iterations; ++it) {
gen_vset();
}
for (int i = 0; i < N; i++) {
RW[i] = W[i] * (RW[i] + R0) / (double)iterations;
}
return ok = true;
}
void MtCarloComputeShare::gen_vset() {
double total_wt = 1.;
int hc = 0;
for (int i = 0; i < K; i++) {
CHECK(total_wt > 0);
double inv_wt = 1. / total_wt;
R0 += inv_wt;
for (int j = 0; j < i; j++) {
RW[A[j]] -= inv_wt;
}
// double p = drand48() * total_wt;
double p = (double)td::Random::fast_uint64() * total_wt / (1. * (1LL << 32) * (1LL << 32));
for (int h = 0; h < hc; h++) {
if (p < H[h].first) {
break;
}
p += H[h].second;
}
int a = -1, b = N, c;
while (b - a > 1) {
c = ((a + b) >> 1);
if (CW[c] <= p) {
a = c;
} else {
b = c;
}
}
CHECK(a >= 0 && a < N);
CHECK(total_wt >= W[a]);
total_wt -= W[a];
double x = CW[a];
c = hc++;
while (c > 0 && H[c - 1].first > x) {
H[c] = H[c - 1];
--c;
}
H[c].first = x;
H[c].second = W[a];
A[i] = a;
}
}
/* /*
* *
* Other block-related functions * Other block-related functions
@ -1723,7 +1807,7 @@ ton::AccountIdPrefixFull interpolate_addr(const ton::AccountIdPrefixFull& src, c
unsigned long long mask = (std::numeric_limits<td::uint64>::max() >> (d - 32)); unsigned long long mask = (std::numeric_limits<td::uint64>::max() >> (d - 32));
return ton::AccountIdPrefixFull{dest.workchain, (dest.account_id_prefix & ~mask) | (src.account_id_prefix & mask)}; return ton::AccountIdPrefixFull{dest.workchain, (dest.account_id_prefix & ~mask) | (src.account_id_prefix & mask)};
} else { } else {
int mask = (-1 >> d); int mask = (int)(~0U >> d);
return ton::AccountIdPrefixFull{(dest.workchain & ~mask) | (src.workchain & mask), src.account_id_prefix}; return ton::AccountIdPrefixFull{(dest.workchain & ~mask) | (src.workchain & mask), src.account_id_prefix};
} }
} }

View file

@ -163,12 +163,12 @@ struct MsgProcessedUpto {
MsgProcessedUpto(ton::ShardId _shard, ton::BlockSeqno _mcseqno, ton::LogicalTime _lt, td::ConstBitPtr _hash) MsgProcessedUpto(ton::ShardId _shard, ton::BlockSeqno _mcseqno, ton::LogicalTime _lt, td::ConstBitPtr _hash)
: shard(_shard), mc_seqno(_mcseqno), last_inmsg_lt(_lt), last_inmsg_hash(_hash) { : shard(_shard), mc_seqno(_mcseqno), last_inmsg_lt(_lt), last_inmsg_hash(_hash) {
} }
bool operator<(const MsgProcessedUpto& other) const & { bool operator<(const MsgProcessedUpto& other) const& {
return shard < other.shard || (shard == other.shard && mc_seqno < other.mc_seqno); return shard < other.shard || (shard == other.shard && mc_seqno < other.mc_seqno);
} }
bool contains(const MsgProcessedUpto& other) const &; bool contains(const MsgProcessedUpto& other) const&;
bool contains(ton::ShardId other_shard, ton::LogicalTime other_lt, td::ConstBitPtr other_hash, bool contains(ton::ShardId other_shard, ton::LogicalTime other_lt, td::ConstBitPtr other_hash,
ton::BlockSeqno other_mc_seqno) const &; ton::BlockSeqno other_mc_seqno) const&;
// NB: this is for checking whether we have already imported an internal message // NB: this is for checking whether we have already imported an internal message
bool already_processed(const EnqueuedMsgDescr& msg) const; bool already_processed(const EnqueuedMsgDescr& msg) const;
bool can_check_processed() const { bool can_check_processed() const {
@ -596,6 +596,62 @@ struct BlockProofChain {
td::Status validate(td::CancellationToken cancellation_token = {}); td::Status validate(td::CancellationToken cancellation_token = {});
}; };
// compute the share of shardchain blocks generated by each validator using Monte Carlo method
class MtCarloComputeShare {
int K, N;
long long iterations;
std::vector<double> W;
std::vector<double> CW, RW;
std::vector<std::pair<double, double>> H;
std::vector<int> A;
double R0;
bool ok;
public:
MtCarloComputeShare(int subset_size, const std::vector<double>& weights, long long iteration_count = 1000000)
: K(subset_size), N((int)weights.size()), iterations(iteration_count), W(weights), ok(false) {
compute();
}
MtCarloComputeShare(int subset_size, int set_size, const double* weights, long long iteration_count = 1000000)
: K(subset_size), N(set_size), iterations(iteration_count), W(weights, weights + set_size), ok(false) {
compute();
}
bool is_ok() const {
return ok;
}
const double* share_array() const {
return ok ? RW.data() : nullptr;
}
const double* weights_array() const {
return ok ? W.data() : nullptr;
}
double operator[](int i) const {
return ok ? RW.at(i) : -1.;
}
double share(int i) const {
return ok ? RW.at(i) : -1.;
}
double weight(int i) const {
return ok ? W.at(i) : -1.;
}
int size() const {
return N;
}
int subset_size() const {
return K;
}
long long performed_iterations() const {
return iterations;
}
private:
bool set_error() {
return ok = false;
}
bool compute();
void gen_vset();
};
int filter_out_msg_queue(vm::AugmentedDictionary& out_queue, ton::ShardIdFull old_shard, ton::ShardIdFull subshard); int filter_out_msg_queue(vm::AugmentedDictionary& out_queue, ton::ShardIdFull old_shard, ton::ShardIdFull subshard);
std::ostream& operator<<(std::ostream& os, const ShardId& shard_id); std::ostream& operator<<(std::ostream& os, const ShardId& shard_id);

View file

@ -272,6 +272,7 @@ transaction$0111 account_addr:bits256 lt:uint64
old:^X new:^X = MERKLE_UPDATE X; old:^X new:^X = MERKLE_UPDATE X;
update_hashes#72 {X:Type} old_hash:bits256 new_hash:bits256 update_hashes#72 {X:Type} old_hash:bits256 new_hash:bits256
= HASH_UPDATE X; = HASH_UPDATE X;
!merkle_proof#03 {X:Type} virtual_hash:bits256 depth:uint16 virtual_root:^X = MERKLE_PROOF X;
acc_trans#5 account_addr:bits256 acc_trans#5 account_addr:bits256
transactions:(HashmapAug 64 ^Transaction CurrencyCollection) transactions:(HashmapAug 64 ^Transaction CurrencyCollection)
@ -731,8 +732,11 @@ top_block_descr_set#4ac789f3 collection:(HashmapE 96 ^TopBlockDescr) = TopBlockD
// //
// VALIDATOR MISBEHAVIOR COMPLAINTS // VALIDATOR MISBEHAVIOR COMPLAINTS
// //
no_blk_gen mc_blk_ref:ExtBlkRef from_utime:uint32 to_utime:uint32 state_proof:^Cell prod_proof:^Cell = ComplaintDescr; prod_info#34 utime:uint32 mc_blk_ref:ExtBlkRef state_proof:^(MERKLE_PROOF Block)
validator_complaint#bc validator_pubkey:uint256 description:^ComplaintDescr created_at:uint32 severity:uint8 reward_addr:uint256 paid:Grams suggested_fine:Grams suggested_fine_part:uint32 = ValidatorComplaint; prod_proof:^(MERKLE_PROOF ShardState) = ProducerInfo;
no_blk_gen from_utime:uint32 prod_info:^ProducerInfo = ComplaintDescr;
no_blk_gen_diff prod_info_old:^ProducerInfo prod_info_new:^ProducerInfo = ComplaintDescr;
validator_complaint#bc validator_pubkey:bits256 description:^ComplaintDescr created_at:uint32 severity:uint8 reward_addr:uint256 paid:Grams suggested_fine:Grams suggested_fine_part:uint32 = ValidatorComplaint;
complaint_status#2d complaint:^ValidatorComplaint voters:(HashmapE 16 True) vset_id:uint256 weight_remaining:int64 = ValidatorComplaintStatus; complaint_status#2d complaint:^ValidatorComplaint voters:(HashmapE 16 True) vset_id:uint256 weight_remaining:int64 = ValidatorComplaintStatus;
// //
@ -808,3 +812,25 @@ cap_method_pubkey#71f4 = SmcCapability;
cap_is_wallet#2177 = SmcCapability; cap_is_wallet#2177 = SmcCapability;
cap_name#ff name:Text = SmcCapability; cap_name#ff name:Text = SmcCapability;
//
// PAYMENT CHANNELS
//
chan_config$_ init_timeout:uint32 close_timeout:uint32 a_key:bits256 b_key:bits256
a_addr:^MsgAddressInt b_addr:^MsgAddressInt channel_id:uint64 = ChanConfig;
chan_state_init$000 signed_A:Bool signed_B:Bool min_A:Grams min_B:Grams expire_at:uint32 A:Grams B:Grams = ChanState;
chan_state_close$001 signed_A:Bool signed_B:Bool promise_A:Grams promise_B:Grams expire_at:uint32 A:Grams B:Grams = ChanState;
chan_state_payout$010 A:Grams B:Grams = ChanState;
chan_promise$_ channel_id:uint64 promise_A:Grams promise_B:Grams = ChanPromise;
chan_signed_promise#_ sig:(Maybe ^bits512) promise:ChanPromise = ChanSignedPromise;
chan_msg_init#27317822 inc_A:Grams inc_B:Grams min_A:Grams min_B:Grams channel_id:uint64 = ChanMsg;
chan_msg_close#f28ae183 extra_A:Grams extra_B:Grams promise:ChanSignedPromise = ChanMsg;
chan_msg_timeout#43278a28 = ChanMsg;
chan_signed_msg$_ sig_A:(Maybe ^bits512) sig_B:(Maybe ^bits512) msg:ChanMsg = ChanSignedMsg;
chan_data$_ config:^ChanConfig state:^ChanState = ChanData;

View file

@ -31,7 +31,7 @@
namespace block { namespace block {
using namespace std::literals::string_literals; using namespace std::literals::string_literals;
td::Status check_block_header_proof(td::Ref<vm::Cell> root, ton::BlockIdExt blkid, ton::Bits256* store_shard_hash_to, td::Status check_block_header_proof(td::Ref<vm::Cell> root, ton::BlockIdExt blkid, ton::Bits256* store_state_hash_to,
bool check_state_hash, td::uint32* save_utime, ton::LogicalTime* save_lt) { bool check_state_hash, td::uint32* save_utime, ton::LogicalTime* save_lt) {
ton::RootHash vhash{root->get_hash().bits()}; ton::RootHash vhash{root->get_hash().bits()};
if (vhash != blkid.root_hash) { if (vhash != blkid.root_hash) {
@ -53,7 +53,7 @@ td::Status check_block_header_proof(td::Ref<vm::Cell> root, ton::BlockIdExt blki
if (save_lt) { if (save_lt) {
*save_lt = info.end_lt; *save_lt = info.end_lt;
} }
if (store_shard_hash_to) { if (store_state_hash_to) {
vm::CellSlice upd_cs{vm::NoVmSpec(), blk.state_update}; vm::CellSlice upd_cs{vm::NoVmSpec(), blk.state_update};
if (!(upd_cs.is_special() && upd_cs.prefetch_long(8) == 4 // merkle update if (!(upd_cs.is_special() && upd_cs.prefetch_long(8) == 4 // merkle update
&& upd_cs.size_ext() == 0x20228)) { && upd_cs.size_ext() == 0x20228)) {
@ -61,11 +61,11 @@ td::Status check_block_header_proof(td::Ref<vm::Cell> root, ton::BlockIdExt blki
} }
auto upd_hash = upd_cs.prefetch_ref(1)->get_hash(0); auto upd_hash = upd_cs.prefetch_ref(1)->get_hash(0);
if (!check_state_hash) { if (!check_state_hash) {
*store_shard_hash_to = upd_hash.bits(); *store_state_hash_to = upd_hash.bits();
} else if (store_shard_hash_to->compare(upd_hash.bits())) { } else if (store_state_hash_to->compare(upd_hash.bits())) {
return td::Status::Error(PSTRING() << "state hash mismatch in block header of " << blkid.to_str() return td::Status::Error(PSTRING() << "state hash mismatch in block header of " << blkid.to_str()
<< " : header declares " << upd_hash.bits().to_hex(256) << " expected " << " : header declares " << upd_hash.bits().to_hex(256) << " expected "
<< store_shard_hash_to->to_hex()); << store_state_hash_to->to_hex());
} }
} }
return td::Status::OK(); return td::Status::OK();

View file

@ -25,7 +25,7 @@ namespace block {
using td::Ref; using td::Ref;
td::Status check_block_header_proof(td::Ref<vm::Cell> root, ton::BlockIdExt blkid, td::Status check_block_header_proof(td::Ref<vm::Cell> root, ton::BlockIdExt blkid,
ton::Bits256* store_shard_hash_to = nullptr, bool check_state_hash = false, ton::Bits256* store_state_hash_to = nullptr, bool check_state_hash = false,
td::uint32* save_utime = nullptr, ton::LogicalTime* save_lt = nullptr); td::uint32* save_utime = nullptr, ton::LogicalTime* save_lt = nullptr);
td::Status check_shard_proof(ton::BlockIdExt blk, ton::BlockIdExt shard_blk, td::Slice shard_proof); td::Status check_shard_proof(ton::BlockIdExt blk, ton::BlockIdExt shard_blk, td::Slice shard_proof);
td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const block::StdAddress& addr, td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const block::StdAddress& addr,

View file

@ -1785,6 +1785,31 @@ std::vector<ton::ValidatorDescr> ValidatorSet::export_validator_set() const {
return l; return l;
} }
std::map<ton::Bits256, int> ValidatorSet::compute_validator_map() const {
std::map<ton::Bits256, int> res;
for (int i = 0; i < (int)list.size(); i++) {
res.emplace(list[i].pubkey.as_bits256(), i);
}
return res;
}
std::vector<double> ValidatorSet::export_scaled_validator_weights() const {
std::vector<double> res;
for (const auto& node : list) {
res.push_back((double)node.weight / (double)total_weight);
}
return res;
}
int ValidatorSet::lookup_public_key(td::ConstBitPtr pubkey) const {
for (int i = 0; i < (int)list.size(); i++) {
if (list[i].pubkey.as_bits256() == pubkey) {
return i;
}
}
return -1;
}
std::vector<ton::ValidatorDescr> Config::do_compute_validator_set(const block::CatchainValidatorsConfig& ccv_conf, std::vector<ton::ValidatorDescr> Config::do_compute_validator_set(const block::CatchainValidatorsConfig& ccv_conf,
ton::ShardIdFull shard, ton::ShardIdFull shard,
const block::ValidatorSet& vset, ton::UnixTime time, const block::ValidatorSet& vset, ton::UnixTime time,

View file

@ -71,6 +71,12 @@ struct ValidatorSet {
} }
const ValidatorDescr& at_weight(td::uint64 weight_pos) const; const ValidatorDescr& at_weight(td::uint64 weight_pos) const;
std::vector<ton::ValidatorDescr> export_validator_set() const; std::vector<ton::ValidatorDescr> export_validator_set() const;
std::map<ton::Bits256, int> compute_validator_map() const;
std::vector<double> export_scaled_validator_weights() const;
int lookup_public_key(td::ConstBitPtr pubkey) const;
int lookup_public_key(const td::Bits256& pubkey) const {
return lookup_public_key(pubkey.bits());
}
}; };
#pragma pack(push, 1) #pragma pack(push, 1)

View file

@ -0,0 +1,199 @@
/*
This file is part of TON Blockchain source code.
TON Blockchain is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
TON Blockchain is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
You must obey the GNU General Public License in all respects for all
of the code used other than OpenSSL. If you modify file(s) with this
exception, you may extend this exception to your version of the file(s),
but you are not obligated to do so. If you do not wish to do so, delete this
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2020 Telegram Systems LLP
*/
#include <iostream>
#include "td/utils/Random.h"
#include "td/utils/misc.h"
#include "block/block.h"
#include <getopt.h>
const int MAX_N = 1000, MAX_K = 100, DEFAULT_K = 7;
int verbosity;
int N, K = DEFAULT_K;
long long iterations = 1000000;
td::uint64 TWL, WL[MAX_N];
double W[MAX_N], CW[MAX_N + 1], RW[MAX_N], R0;
int A[MAX_N], C[MAX_N];
long long TC;
void gen_vset() {
static std::pair<double, double> H[MAX_N];
double total_wt = 1.;
int hc = 0;
for (int i = 0; i < K; i++) {
CHECK(total_wt > 0);
double inv_wt = 1. / total_wt;
R0 += inv_wt; // advanced mtcarlo stats
for (int j = 0; j < i; j++) {
RW[A[j]] -= inv_wt; // advanced mtcarlo stats
}
// double p = drand48() * total_wt;
double p = (double)td::Random::fast_uint64() * total_wt / (1. * (1LL << 32) * (1LL << 32));
for (int h = 0; h < hc; h++) {
if (p < H[h].first) {
break;
}
p += H[h].second;
}
int a = -1, b = N, c;
while (b - a > 1) {
c = ((a + b) >> 1);
if (CW[c] <= p) {
a = c;
} else {
b = c;
}
}
CHECK(a >= 0 && a < N);
CHECK(total_wt >= W[a]);
total_wt -= W[a];
double x = CW[a];
c = hc++;
while (c > 0 && H[c - 1].first > x) {
H[c] = H[c - 1];
--c;
}
H[c].first = x;
H[c].second = W[a];
A[i] = a;
C[a]++; // simple mtcarlo stats
// std::cout << a << ' ';
}
// std::cout << std::endl;
++TC; // simple mtcarlo stats
}
void mt_carlo() {
for (int i = 0; i < N; i++) {
C[i] = 0;
RW[i] = 0.;
}
TC = 0;
R0 = 0.;
std::cout << "running " << iterations << " steps of Monte Carlo simulation\n";
for (long long it = 0; it < iterations; ++it) {
gen_vset();
}
for (int i = 0; i < N; i++) {
RW[i] = W[i] * (RW[i] + R0) / (double)iterations;
}
}
double B[MAX_N];
void compute_bad_approx() {
static double S[MAX_K + 1];
S[0] = 1.;
for (int i = 1; i <= K; i++) {
S[i] = 0.;
}
for (int i = 0; i < N; i++) {
double p = W[i];
for (int j = K; j > 0; j--) {
S[j] += p * S[j - 1];
}
}
double Sk = S[K];
for (int i = 0; i < N; i++) {
double t = 1., p = W[i];
for (int j = 1; j <= K; j++) {
t = S[j] - p * t;
}
B[i] = 1. - t / Sk;
}
}
void usage() {
std::cout
<< "usage: test-weight-distr [-k<shard-val-num>][-m<iterations>][-s<rand-seed>]\nReads the set of validator "
"weights from stdin and emulates validator shard distribution load\n\t-k <shard-val-num>\tSets the number of "
"validators generating each shard\n\t-m <iterations>\tMonte Carlo simulation steps\n";
std::exit(2);
}
int main(int argc, char* const argv[]) {
int i;
int new_verbosity_level = VERBOSITY_NAME(INFO);
// long seed = 0;
while ((i = getopt(argc, argv, "hs:k:m:v:")) != -1) {
switch (i) {
case 'k':
K = td::to_integer<int>(td::Slice(optarg));
CHECK(K > 0 && K <= 100);
break;
case 'm':
iterations = td::to_integer<long long>(td::Slice(optarg));
CHECK(iterations > 0);
break;
case 's':
// seed = td::to_integer<long>(td::Slice(optarg));
// srand48(seed);
break;
case 'v':
new_verbosity_level = VERBOSITY_NAME(FATAL) + (verbosity = td::to_integer<int>(td::Slice(optarg)));
break;
case 'h':
usage();
std::exit(2);
default:
usage();
std::exit(2);
}
}
SET_VERBOSITY_LEVEL(new_verbosity_level);
for (N = 0; N < MAX_N && (std::cin >> WL[N]); N++) {
CHECK(WL[N] > 0);
TWL += WL[N];
}
CHECK(std::cin.eof());
CHECK(N > 0 && TWL > 0 && N <= MAX_N);
K = std::min(K, N);
CHECK(K > 0 && K <= MAX_K);
double acc = 0.;
for (i = 0; i < N; i++) {
CW[i] = acc;
acc += W[i] = (double)WL[i] / (double)TWL;
std::cout << "#" << i << ":\t" << W[i] << std::endl;
}
compute_bad_approx();
mt_carlo();
std::cout << "result of Monte Carlo simulation (" << iterations << " iterations):" << std::endl;
std::cout << "idx\tweight\tmtcarlo1\tmtcarlo2\tapprox\n";
for (i = 0; i < N; i++) {
std::cout << "#" << i << ":\t" << W[i] << '\t' << (double)C[i] / (double)iterations << '\t' << RW[i] << '\t' << B[i]
<< std::endl;
}
// same computation, but using a MtCarloComputeShare object
block::MtCarloComputeShare MT(K, N, W, iterations);
std::cout << "-----------------------\n";
for (i = 0; i < N; i++) {
std::cout << '#' << i << ":\t" << MT.weight(i) << '\t' << MT.share(i) << std::endl;
}
return 0;
}

View file

@ -1429,6 +1429,9 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
if (!tlb::csr_unpack(msg.info, erec)) { if (!tlb::csr_unpack(msg.info, erec)) {
return -1; return -1;
} }
if (act_rec.mode & ~3) {
return -1; // invalid mode for an external message
}
info.src = std::move(erec.src); info.src = std::move(erec.src);
info.dest = std::move(erec.dest); info.dest = std::move(erec.dest);
// created_lt and created_at are ignored // created_lt and created_at are ignored

View file

@ -272,7 +272,7 @@ class AnyIntView {
int parse_binary_any(const char* str, int str_len, int* frac = nullptr); int parse_binary_any(const char* str, int str_len, int* frac = nullptr);
std::string to_dec_string_destroy_any(); std::string to_dec_string_destroy_any();
std::string to_dec_string_slow_destroy_any(); std::string to_dec_string_slow_destroy_any();
std::string to_hex_string_any(bool upcase = false) const; std::string to_hex_string_any(bool upcase = false, int zero_pad = 0) const;
std::string to_hex_string_slow_destroy_any(); std::string to_hex_string_slow_destroy_any();
std::string to_binary_string_any() const; std::string to_binary_string_any() const;
@ -650,7 +650,7 @@ class BigIntG {
std::string to_dec_string_destroy(); std::string to_dec_string_destroy();
std::string to_dec_string_slow() const; std::string to_dec_string_slow() const;
std::string to_hex_string_slow() const; std::string to_hex_string_slow() const;
std::string to_hex_string(bool upcase = false) const; std::string to_hex_string(bool upcase = false, int zero_pad = 0) const;
std::string to_binary_string() const; std::string to_binary_string() const;
double to_double() const { double to_double() const {
return is_valid() ? ldexp(top_double(), (n - 1) * word_shift) : NAN; return is_valid() ? ldexp(top_double(), (n - 1) * word_shift) : NAN;
@ -2290,16 +2290,19 @@ std::string AnyIntView<Tr>::to_hex_string_slow_destroy_any() {
} }
template <class Tr> template <class Tr>
std::string AnyIntView<Tr>::to_hex_string_any(bool upcase) const { std::string AnyIntView<Tr>::to_hex_string_any(bool upcase, int zero_pad) const {
if (!is_valid()) { if (!is_valid()) {
return "NaN"; return "NaN";
} }
int s = sgn(), k = 0; int s = sgn(), k = 0;
if (!s) { if (!s) {
if (zero_pad > 0) {
return std::string(zero_pad, '0');
}
return "0"; return "0";
} }
std::string x; std::string x;
x.reserve(((size() * word_shift + word_bits) >> 2) + 2); x.reserve(2 + std::max((size() * word_shift + word_bits) >> 2, zero_pad));
assert(word_shift < word_bits - 4); assert(word_shift < word_bits - 4);
const char* hex_digs = (upcase ? HEX_digits : hex_digits); const char* hex_digs = (upcase ? HEX_digits : hex_digits);
word_t v = 0; word_t v = 0;
@ -2317,6 +2320,11 @@ std::string AnyIntView<Tr>::to_hex_string_any(bool upcase) const {
x += hex_digs[v & 15]; x += hex_digs[v & 15];
v >>= 4; v >>= 4;
} }
if (zero_pad > 0) {
while (x.size() < (unsigned)zero_pad) {
x += '0';
}
}
if (s < 0) { if (s < 0) {
x += '-'; x += '-';
} }
@ -2498,8 +2506,8 @@ std::string BigIntG<len, Tr>::to_hex_string_slow() const {
} }
template <int len, class Tr> template <int len, class Tr>
std::string BigIntG<len, Tr>::to_hex_string(bool upcase) const { std::string BigIntG<len, Tr>::to_hex_string(bool upcase, int zero_pad) const {
return as_any_int().to_hex_string_any(upcase); return as_any_int().to_hex_string_any(upcase, zero_pad);
} }
template <int len, class Tr> template <int len, class Tr>

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 2020 Telegram Systems LLP
*/
#pragma once
#include "refcnt.hpp"
#include "td/actor/PromiseFuture.h"
namespace td {
template <typename S, typename T>
class BinaryPromiseMerger : public CntObject {
Result<S> first_;
Result<T> second_;
Promise<std::pair<S, T>> promise_;
std::atomic<int> pending_;
public:
BinaryPromiseMerger(Promise<std::pair<S, T>> promise) : promise_(std::move(promise)), pending_(2) {
}
static std::pair<Promise<S>, Promise<T>> split(Promise<std::pair<S, T>> promise) {
auto ref = make_ref<BinaryPromiseMerger>(std::move(promise));
auto& obj = ref.write();
return std::make_pair(obj.left(), obj.right());
}
private:
Promise<S> left() {
return [this, self = Ref<BinaryPromiseMerger>(this)](Result<S> res) {
first_ = std::move(res);
work();
};
}
Promise<T> right() {
return [this, self = Ref<BinaryPromiseMerger>(this)](Result<T> res) {
second_ = std::move(res);
work();
};
}
void work() {
if (!--pending_) {
if (first_.is_error()) {
promise_.set_error(first_.move_as_error());
} else if (second_.is_error()) {
promise_.set_error(second_.move_as_error());
} else {
promise_.set_result(std::pair<S, T>(first_.move_as_ok(), second_.move_as_ok()));
}
}
}
};
template <typename S, typename T>
std::pair<Promise<S>, Promise<T>> split_promise(Promise<std::pair<S, T>> promise) {
return BinaryPromiseMerger<S, T>::split(std::move(promise));
}
} // namespace td

View file

@ -287,8 +287,8 @@ std::string dec_string2(RefInt256&& x) {
return x.is_null() ? "(null)" : (x.is_unique() ? x.unique_write().to_dec_string_destroy() : x->to_dec_string()); return x.is_null() ? "(null)" : (x.is_unique() ? x.unique_write().to_dec_string_destroy() : x->to_dec_string());
} }
std::string hex_string(RefInt256 x, bool upcase) { std::string hex_string(RefInt256 x, bool upcase, int zero_pad) {
return x.is_null() ? "(null)" : x->to_hex_string(upcase); return x.is_null() ? "(null)" : x->to_hex_string(upcase, zero_pad);
} }
std::string binary_string(RefInt256 x) { std::string binary_string(RefInt256 x) {

View file

@ -120,7 +120,7 @@ extern RefInt256 bits_to_refint(td::ConstBitPtr bits, int n, bool sgnd = false);
extern std::string dec_string(RefInt256 x); extern std::string dec_string(RefInt256 x);
extern std::string dec_string2(RefInt256&& x); extern std::string dec_string2(RefInt256&& x);
extern std::string hex_string(RefInt256 x, bool upcase = false); extern std::string hex_string(RefInt256 x, bool upcase = false, int zero_pad = 0);
extern std::string binary_string(RefInt256 x); extern std::string binary_string(RefInt256 x);
extern RefInt256 dec_string_to_int256(const std::string& s); extern RefInt256 dec_string_to_int256(const std::string& s);

View file

@ -36,6 +36,7 @@ library TonUtil // TON Blockchain Fift Library
// ( x -- ) Displays a 64-digit hex number // ( x -- ) Displays a 64-digit hex number
{ 64 0x. } : 64x. { 64 0x. } : 64x.
{ 64 0X. } : 64X.
// ( wc addr -- ) Show address in <workchain>:<account> form // ( wc addr -- ) Show address in <workchain>:<account> form
{ swap ._ .":" 64x. } : .addr { swap ._ .":" 64x. } : .addr
// ( wc addr flags -- ) Show address in base64url form // ( wc addr flags -- ) Show address in base64url form
@ -205,6 +206,13 @@ recursive append-long-bytes {
{ 256 u>B B{3ee6} swap B+ dup crc16 16 u>B B+ B>base64 } : pubkey>$ { 256 u>B B{3ee6} swap B+ dup crc16 16 u>B B+ B>base64 } : pubkey>$
{ pubkey>$ type } : .pubkey { pubkey>$ type } : .pubkey
// ( S -- x ) parse validator-encoded public key
{ base64>B dup Blen 36 <> abort"public key with magic must be 36 bytes long"
4 B| swap 32 B>u@ 0xC6B41348 <> abort"unknown magic for public key (not Ed25519)"
} : parse-val-pubkey
{ bl word parse-val-pubkey 1 'nop } ::_ VPK'
{ char } word base64>B 1 'nop } ::_ B64{
// adnl address parser // adnl address parser
{ 256 u>B B{2D} swap B+ dup crc16 16 u>B B+ } : adnl-preconv { 256 u>B B{2D} swap B+ dup crc16 16 u>B B+ } : adnl-preconv
{ swap 32 /mod dup 26 < { 65 } { 24 } cond + rot swap hold } : Base32# { swap 32 /mod dup 26 < { 65 } { 24 } cond + rot swap hold } : Base32#

View file

@ -62,15 +62,31 @@ variable validators-weight
validators-weight 0! validators-weight 0!
{ validator-dict @ second } : validator# { validator-dict @ second } : validator#
// val-pubkey weight --
{ dup 0<= abort"validator weight must be non-negative" { dup 0<= abort"validator weight must be non-negative"
dup 64 ufits not abort"validator weight must fit into 64 bits" 64 ufits not abort"validator weight must fit into 64 bits"
} : check-val-weight
// ( val-pubkey weight -- c )
{ dup check-val-weight
over Blen 32 <> abort"validator public key must be 32 bytes long" over Blen 32 <> abort"validator public key must be 32 bytes long"
tuck <b x{538e81278a} s, rot B, swap 64 u, b> <s <b x{538e81278a} s, rot B, swap 64 u, b>
validator-dict 2@ dup 1+ 3 -roll swap } : serialize-validator
// ( val-pubkey adnl weight -- c )
{ dup check-val-weight
over 256 ufits not abort"adnl address must fit into 256 bits"
rot dup Blen 32 <> abort"validator public key must be 32 bytes long"
<b x{738e81278a} s, swap B, swap 64 u, swap 256 u, b>
} : serialize-adnl-validator
// ( weight val-cell -- )
{ swap validators-weight +!
<s validator-dict 2@ dup 1+ 3 -roll swap
16 udict!+ 0= abort"cannot add validator" 16 udict!+ 0= abort"cannot add validator"
swap validator-dict 2! validators-weight +! swap validator-dict 2!
} : add-validator } : register-validator
// val-pubkey weight --
{ tuck serialize-validator register-validator } : add-validator
// val-pubkey adnl-addr weight --
{ -rot 2 pick serialize-adnl-validator register-validator
} : add-adnl-validator
// since-ut until-ut main-val-cnt-or-0 -- // since-ut until-ut main-val-cnt-or-0 --
{ ?dup 0= { validator# } if { ?dup 0= { validator# } if
validator# 0= abort"no initial validators defined" validator# 0= abort"no initial validators defined"
@ -230,21 +246,24 @@ variable special-dict
} : create-wallet1 } : create-wallet1
// D x t -- D' // D x t -- D'
{ <b rot Gram, swap rot 16 b>idict! not abort"cannot add value" { <b rot Gram, swap rot 32 b>idict! not abort"cannot add value"
} : rdict-entry } : rdict-entry
{ 86400 * } : days*
// balance -- dict // balance -- dict
{ dictnew { dictnew
over -32768 rdict-entry over 31 -1<< rdict-entry
over 3/4 */ 92 rdict-entry over 3/4 */ 91 days* rdict-entry
over 1/2 */ 183 rdict-entry over 1/2 */ 183 days* rdict-entry
swap 1/4 */ 366 rdict-entry swap 1/4 */ 365 days* rdict-entry
0 548 rdict-entry 0 548 days* rdict-entry
} : make-rdict } : make-rdict
variable wallet2-start-at wallet2-start-at 0!
now 86400 / 1+ 86400 * wallet2-start-at !
// pubkey amount -- // pubkey amount --
{ over ."Key " pubkey>$ type ." -> " { over ."Key " pubkey>$ type ." -> "
RWCode2 // code RWCode2 // code
<b 1 32 u, 3 pick 256 u, 3 roll make-rdict dict, b> // data <b 1 32 u, 3 pick 256 u, 3 roll wallet2-start-at @ 32 u, make-rdict dict, b> // data
empty_cell // libs empty_cell // libs
3 roll // balance 3 roll // balance
0 // split_depth 0 // split_depth

View file

@ -0,0 +1,29 @@
#!/usr/bin/fift -s
"TonUtil.fif" include
"GetOpt.fif" include
"validator-to-sign.req" =: savefile
{ ."usage: " @' $0 type ." <validator-idx> <elect-id> <complaint-hash> [<savefile>]" cr
."Creates an unsigned request to vote for complaint <complaint-hash> (decimal; prefix with '0x' if needed) of past validator set <elect-id> on behalf of validator with zero-based index <validator-idx> in current validator set (as stored in configuration parameter 34)." cr
."The result is saved into <savefile> (" savefile type ." by default) and output in hexadecimal form, to be signed later by the validator public key" cr 1 halt
} : usage
$# dup 3 < swap 4 > or ' usage if
4 :$1..n
$1 parse-int dup =: val-idx
16 ufits not abort"validator index out of range"
$2 parse-int dup =: elect-id
32 ufits not abort"invalid election id"
$3 parse-int dup =: compl-hash
256 ufits not abort"invalid complaint hash"
$4 savefile replace-if-null =: savefile
."Creating a request to vote for complaint 0x" compl-hash 64x. ."of past validator set " elect-id .
."on behalf of current validator with index " val-idx . cr
B{56744350} val-idx 16 u>B B+ elect-id 32 u>B B+ compl-hash 256 u>B B+
dup Bx. cr
dup B>base64url type cr
savefile tuck B>file ."Saved to file " type cr

View file

@ -0,0 +1,44 @@
#!/usr/bin/fift -s
"TonUtil.fif" include
"GetOpt.fif" include
"vote-query.boc" =: savefile
{ ."usage: " @' $0 type ." <validator-idx> <elect-id> <complaint-hash> <validator-pubkey> <validator-signature> [<savefile>]" cr
."Creates an internal message body to be sent from any smart contract residing in the masterchain to the elections smart contract containing a signed request to vote for complaint <complaint-hash> (decimal; prefix with '0x' if needed) of past validator set <elect-id> on behalf of current validator with zero-based index <validator-idx> and (Base64) public key <validator-pubkey> in current validator set (as stored in configuration parameter 34)" cr
."<validator-signature> must be the base64 representation of Ed25519 signature of the previously generated unsigned request by means of <validator-pubkey>" cr
."The result is saved into <savefile> (" savefile type ." by default), to be embedded later into an internal message" cr 1 halt
} : usage
$# dup 5 < swap 6 > or ' usage if
6 :$1..n
$1 parse-int dup =: val-idx
16 ufits not abort"validator index out of range"
$2 parse-int dup =: elect-id
32 ufits not abort"invalid election id"
$3 parse-int dup =: compl-hash
256 ufits not abort"invalid complaint hash"
$4 base64>B dup Blen 36 <> abort"validator Ed25519 public key must be exactly 36 bytes long"
32 B>u@+ 0xC6B41348 <> abort"invalid Ed25519 public key: unknown magic number"
=: pubkey
$5 base64>B dup Blen 64 <> abort"validator Ed25519 signature must be exactly 64 bytes long"
=: signature
$6 savefile replace-if-null =: savefile
."Creating the body of an internal message to be sent to the elections smart contract" cr }
."containing a signed request to vote for complaint 0x" compl-hash 64x. ."of past validator set " elect-id .
."on behalf of current validator with index " val-idx . "and public key" pubkey Bx. cr
B{56744350} val-idx 16 u>B B+ elect-id 32 u>B B+ compl-hash 256 u>B B+ dup =: to_sign
."String to sign is " Bx. cr
to_sign signature pubkey ed25519_chksign not abort"Ed25519 signature is invalid"
."Provided a valid Ed25519 signature " signature Bx. ." with validator public key " pubkey Bx. cr
now 32 << compl-hash 32 1<< mod + =: query-id
<b x{56744370} s, query-id 64 u, signature B, to_sign B, b>
."Internal message body is " dup <s csr. cr
2 boc+>B savefile tuck B>file ."Saved to file " type cr

View file

@ -1097,8 +1097,21 @@ tuple past_elections() method_id {
do { do {
(id, var fs, var found) = past_elections.udict_get_prev?(32, id); (id, var fs, var found) = past_elections.udict_get_prev?(32, id);
if (found) { if (found) {
var info = [unpack_past_election(fs)]; list = cons([id, unpack_past_election(fs)], list);
list = cons(pair(id, info), list); }
} until (~ found);
return list;
}
tuple past_elections_list() method_id {
var (elect, credits, past_elections, grams, active_id, active_hash) = load_data();
var id = (1 << 32);
var list = null();
do {
(id, var fs, var found) = past_elections.udict_get_prev?(32, id);
if (found) {
var (unfreeze_at, stake_held, vset_hash, frozen_dict, total_stake, bonuses, complaints) = unpack_past_election(fs);
list = cons([id, unfreeze_at, vset_hash, stake_held], list);
} }
} until (~ found); } until (~ found);
return list; return list;
@ -1117,7 +1130,7 @@ _ complete_unpack_complaint(slice cs) inline_ref {
return [[complaint.begin_parse().unpack_complaint()], voters_list, vset_id, weight_remaining]; return [[complaint.begin_parse().unpack_complaint()], voters_list, vset_id, weight_remaining];
} }
cell get_past_complaints(int election_id) inline_ref { cell get_past_complaints(int election_id) inline_ref method_id {
var (elect, credits, past_elections, grams, active_id, active_hash) = load_data(); var (elect, credits, past_elections, grams, active_id, active_hash) = load_data();
var (fs, found?) = past_elections.udict_get?(32, election_id); var (fs, found?) = past_elections.udict_get?(32, election_id);
ifnot (found?) { ifnot (found?) {
@ -1145,3 +1158,11 @@ tuple list_complaints(int election_id) method_id {
} until (~ found?); } until (~ found?);
return list; return list;
} }
int complaint_storage_price(int bits, int refs, int expire_in) method_id {
;; compute complaint storage/creation price
var (deposit, bit_price, cell_price) = get_complaint_prices();
var pps = (bits + 1024) * bit_price + (refs + 2) * cell_price;
var paid = pps * expire_in + deposit;
return paid + (1 << 30);
}

View file

@ -0,0 +1,50 @@
#!/usr/bin/fift -s
"TonUtil.fif" include
"GetOpt.fif" include
{ show-options-help 1 halt } : usage
86400 3 * =: expire-in
false =: critical
-1 =: old-hash
begin-options
" <election-id> <complaint-boc> [-x <expire-in>] [<savefile>]" +cr +tab
+"Embeds a validator complaint loaded from file <complaint-boc> into an internal message body to be sent to the elector smart contract "
+"and saves it as an internal message body into <savefile>.boc ('complaint-msg-body.boc' by default)"
disable-digit-options generic-help-setopt
"x" "--expires-in" { parse-int =: expire-in } short-long-option-arg
"Sets complaint expiration time in seconds (default " expire-in (.) $+ +")" option-help
"h" "--help" { usage } short-long-option
"Shows a help message" option-help
parse-options
$# dup 2 < swap 3 > or ' usage if
3 :$1..n
$1 parse-int dup =: election-id
32 ufits not abort"invalid election id"
$2 =: boc-filename
$3 "complaint-msg-body.boc" replace-if-null =: savefile
expire-in now + =: expire-at
boc-filename dup ."Loading complaint from file `" type ."`" cr
file>B B>boc dup =: complaint hash =: c-hash
complaint <s 8 u@ 0xbc <> abort"Not a valid ValidatorComplaint"
complaint <s csr.
." complaint envelope will expire at " expire-at . ."(in " expire-in . ."seconds)" cr
now 32 << c-hash 0xffffffff and or =: query-id
."Query id is " query-id . cr
<b x{52674370} s, query-id 64 u, election-id 32 u, expire-at 32 u,
complaint <s s, b>
dup ."resulting internal message body: " <s csr. cr
2 boc+>B dup Bx. cr
complaint
totalcsize swap ."(a total of " . ."data bits, " . ."cell references -> "
drop dup Blen . ."BoC data bytes)" cr
savefile tuck B>file
."(Saved to file " type .")" cr

View file

@ -29,8 +29,8 @@ def? $3 { @' $3 } { "rwallet" } cond constant file-base
} : make-rdict } : make-rdict
// Create new restricted wallet; code taken from `auto/restricted-wallet2-code.fif` // Create new restricted wallet; code taken from `auto/restricted-wallet2-code.fif`
"auto/restricted-wallet-code.fif" include // code "auto/restricted-wallet2-code.fif" include // code
<b 0 32 u, PubKey 256 u, amount make-rdict dict, b> // data <b 0 32 u, PubKey 256 u, 0 32 u, amount make-rdict dict, b> // data
null // no libraries null // no libraries
<b b{0011} s, 3 roll ref, rot ref, swap dict, b> // create StateInit <b b{0011} s, 3 roll ref, rot ref, swap dict, b> // create StateInit
dup ."StateInit: " <s csr. cr dup ."StateInit: " <s csr. cr

View file

@ -0,0 +1,315 @@
;; WARINIG: NOT READY FOR A PRODUCTION!
int err:wrong_a_signature() asm "31 PUSHINT";
int err:wrong_b_signature() asm "32 PUSHINT";
int err:msg_value_too_small() asm "33 PUSHINT";
int err:replay_protection() asm "34 PUSHINT";
int err:no_timeout() asm "35 PUSHINT";
int err:expected_init() asm "36 PUSHINT";
int err:expected_close() asm "37 PUSHINT";
int err:no_promise_signature() asm "38 PUSHINT";
int err:wrong_channel_id() asm "39 PUSHINT";
int msg:init() asm "0x27317822 PUSHINT";
int msg:close() asm "0xf28ae183 PUSHINT";
int msg:timeout() asm "0x43278a28 PUSHINT";
int msg:payout() asm "0x37fe7810 PUSHINT";
int state:init() asm "0 PUSHINT";
int state:close() asm "1 PUSHINT";
int state:payout() asm "2 PUSHINT";
;; A - initial balance of Alice,
;; B - initial balance of B
;;
;; To determine balance we track nondecreasing list of promises
;; promise_A ;; promised by Alice to Bob
;; promise_B ;; promised by Bob to Alice
;;
;; diff - balance between Alice and Bob. 0 in the beginning
;; diff = promise_B - promise_A;
;; diff = clamp(diff, -A, +B);
;;
;; final_A = A + diff;
;; final_B = B + diff;
;; Data pack/unpack
;;
_ unpack_data() inline_ref {
var cs = get_data().begin_parse();
var res = (cs~load_ref(), cs~load_ref());
cs.end_parse();
return res;
}
_ pack_data(cell config, cell state) impure inline_ref {
set_data(begin_cell().store_ref(config).store_ref(state).end_cell());
}
;; Config pack/unpack
;;
;; config$_ initTimeout:int exitTimeout:int a_key:int256 b_key:int256 a_addr b_addr channel_id:uint64 = Config;
;;
_ unpack_config(cell config) {
var cs = config.begin_parse();
var res = (
cs~load_uint(32),
cs~load_uint(32),
cs~load_uint(256),
cs~load_uint(256),
cs~load_ref().begin_parse(),
cs~load_ref().begin_parse(),
cs~load_uint(64));
cs.end_parse();
return res;
}
;; takes
;; signedMesage$_ a_sig:Maybe<int256> b_sig:Maybe<int256> msg:Message = SignedMessage;
;; checks signatures and unwap message.
(slice, (int, int)) unwrap_signatures(slice cs, int a_key, int b_key) {
int a? = cs~load_int(1);
slice a_sig = cs;
if (a?) {
a_sig = cs~load_ref().begin_parse().preload_bits(512);
}
var b? = cs~load_int(1);
slice b_sig = cs;
if (b?) {
b_sig = cs~load_ref().begin_parse().preload_bits(512);
}
int hash = cs.slice_hash();
if (a?) {
throw_unless(err:wrong_a_signature(), check_signature(hash, a_sig, a_key));
}
if (b?) {
throw_unless(err:wrong_b_signature(), check_signature(hash, b_sig, b_key));
}
return (cs, (a?, b?));
}
;; process message, give state is stateInit
;;
;; stateInit signed_A?:Bool signed_B?:Bool min_A:Grams min_B:Grams expire_at:uint32 A:Grams B:Grams = State;
_ unpack_state_init(slice state) {
return (
state~load_int(1),
state~load_int(1),
state~load_grams(),
state~load_grams(),
state~load_uint(32),
state~load_grams(),
state~load_grams());
}
_ pack_state_init(int signed_A?, int signed_B?, int min_A, int min_B, int expire_at, int A, int B) {
return begin_cell()
.store_int(state:init(), 3)
.store_int(signed_A?, 1)
.store_int(signed_B?, 1)
.store_grams(min_A)
.store_grams(min_B)
.store_uint(expire_at, 32)
.store_grams(A)
.store_grams(B).end_cell();
}
;; stateClosing$10 signed_A?:bool signed_B?:Bool promise_A:Grams promise_B:Grams exipire_at:uint32 A:Grams B:Grams = State;
_ unpack_state_close(slice state) {
return (
state~load_int(1),
state~load_int(1),
state~load_grams(),
state~load_grams(),
state~load_uint(32),
state~load_grams(),
state~load_grams());
}
_ pack_state_close(int signed_A?, int signed_B?, int promise_A, int promise_B, int expire_at, int A, int B) {
return begin_cell()
.store_int(state:close(), 3)
.store_int(signed_A?, 1)
.store_int(signed_B?, 1)
.store_grams(promise_A)
.store_grams(promise_B)
.store_uint(expire_at, 32)
.store_grams(A)
.store_grams(B).end_cell();
}
_ send_payout(slice s_addr, int amount, int channel_id, int flags) impure {
send_raw_message(begin_cell()
.store_uint(0x10, 6)
.store_slice(s_addr)
.store_grams(amount)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.store_uint(msg:payout(), 32)
.store_uint(channel_id, 64)
.end_cell(), flags);
}
cell do_payout(int promise_A, int promise_B, int A, int B, slice a_addr, slice b_addr, int channel_id) impure {
accept_message();
int diff = promise_B - promise_A;
if (diff < - A) {
diff = - A;
}
if (diff > B) {
diff = B;
}
A += diff;
B -= diff;
send_payout(a_addr, A, channel_id, 3);
send_payout(b_addr, B, channel_id, 3);
return begin_cell()
.store_int(state:payout(), 3)
.store_grams(A)
.store_grams(B)
.end_cell();
}
;;
;; init$000 inc_A:Grams inc_B:Grams min_A:Grams min_B:Grams = Message;
;;
cell with_init(slice state, int msg_value, slice msg, int msg_signed_A?, int msg_signed_B?,
slice a_addr, slice b_addr, int init_timeout, int channel_id) {
;; parse state
(int signed_A?, int signed_B?, int min_A, int min_B, int expire_at, int A, int B) = unpack_state_init(state);
if (expire_at == 0) {
expire_at = now() + init_timeout;
}
int op = msg~load_uint(32);
if (op == msg:timeout()) {
throw_unless(err:no_timeout(), expire_at < now());
return do_payout(0, 0, A, B, a_addr, b_addr, channel_id);
}
throw_unless(err:expected_init(), op == msg:init());
;; unpack init message
(int inc_A, int inc_B, int upd_min_A, int upd_min_B, int got_channel_id) =
(msg~load_grams(), msg~load_grams(), msg~load_grams(), msg~load_grams(), msg~load_uint(64));
throw_unless(err:wrong_channel_id(), got_channel_id == channel_id);
;; TODO: we should reserve some part of the value for comission
throw_if(err:msg_value_too_small(), msg_value < inc_A + inc_B);
throw_unless(err:replay_protection(), (msg_signed_A? < signed_A?) | (msg_signed_B? < signed_B?));
A += inc_A;
B += inc_B;
signed_A? |= msg_signed_A?;
if (min_A < upd_min_A) {
min_A = upd_min_A;
}
signed_B? |= msg_signed_B?;
if (min_B < upd_min_B) {
min_B = upd_min_B;
}
if (signed_A? & signed_B?) {
if ((min_A > A) | (min_B > B)) {
return do_payout(0, 0, A, B, a_addr, b_addr, channel_id);
}
return pack_state_close(0, 0, 0, 0, 0, A, B);
}
return pack_state_init(signed_A?, signed_B?, min_A, min_B, expire_at, A, B);
}
;; close$001 extra_A:Grams extra_B:Grams sig:Maybe<int256> promise_A:Grams promise_B:Grams = Message;
cell with_close(slice cs, slice msg, int msg_signed_A?, int msg_signed_B?, int a_key, int b_key,
slice a_addr, slice b_addr, int expire_timeout, int channel_id) {
;; parse state
(int signed_A?, int signed_B?, int promise_A, int promise_B, int expire_at, int A, int B) = unpack_state_close(cs);
if (expire_at == 0) {
expire_at = now() + expire_timeout;
}
int op = msg~load_uint(32);
if (op == msg:timeout()) {
throw_unless(err:no_timeout(), expire_at < now());
return do_payout(promise_A, promise_B, A, B, a_addr, b_addr, channel_id);
}
throw_unless(err:expected_close(), op == msg:close());
;; also ensures that (msg_signed_A? | msg_signed_B?) is true
throw_unless(err:replay_protection(), (msg_signed_A? < signed_A?) | (msg_signed_B? < signed_B?));
signed_A? |= msg_signed_A?;
signed_B? |= msg_signed_B?;
;; unpack close message
(int extra_A, int extra_B) = (msg~load_grams(), msg~load_grams());
int has_sig = msg~load_int(1);
if (has_sig) {
slice sig = msg~load_ref().begin_parse().preload_bits(512);
int hash = msg.slice_hash();
ifnot (msg_signed_A?) {
throw_unless(err:wrong_a_signature(), check_signature(hash, sig, a_key));
extra_A = 0;
}
ifnot (msg_signed_B?) {
throw_unless(err:wrong_b_signature(), check_signature(hash, sig, b_key));
extra_B = 0;
}
} else {
throw_unless(err:no_promise_signature(), msg_signed_A? & msg_signed_B?);
extra_A = 0;
extra_B = 0;
}
(int got_channel_id, int update_promise_A, int update_promise_B) = (msg~load_uint(64), msg~load_grams(), msg~load_grams());
throw_unless(err:wrong_channel_id(), got_channel_id == channel_id);
accept_message();
update_promise_A += extra_A;
if (promise_A < update_promise_A) {
promise_A = update_promise_A;
}
update_promise_B += extra_B;
if (promise_B < update_promise_B) {
promise_B = update_promise_B;
}
if (signed_A? & signed_B?) {
return do_payout(promise_A, promise_B, A, B, a_addr, b_addr, channel_id);
}
return pack_state_close(signed_A?, signed_B?, promise_A, promise_B, expire_at, A, B);
}
() recv_any(int msg_value, slice msg) impure {
(cell config, cell state) = unpack_data();
(int init_timeout, int close_timeout, int a_key, int b_key, slice a_addr, slice b_addr, int channel_id) = config.unpack_config();
(int msg_signed_A?, int msg_signed_B?) = msg~unwrap_signatures(a_key, b_key);
slice cs = state.begin_parse();
int state_type = cs~load_uint(3);
if (state_type == state:init()) { ;; init
state = with_init(cs, msg_value, msg, msg_signed_A?, msg_signed_B?, a_addr, b_addr, init_timeout, channel_id);
} if (state_type == state:close()) {
state = with_close(cs, msg, msg_signed_A?, msg_signed_B?, a_key, b_key, a_addr, b_addr, close_timeout, channel_id);
}
pack_data(config, state);
}
() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure {
recv_any(msg_value, in_msg);
}
() recv_external(slice in_msg) impure {
recv_any(0, in_msg);
}

View file

@ -1,13 +1,16 @@
;; Restricted wallet (a variant of wallet-code.fc) ;; Restricted wallet (a variant of wallet-code.fc)
;; until configuration parameter -13 is set, accepts messages only to elector smc ;; restricts access to parts of balance until certain dates
() recv_internal(slice in_msg) impure { () recv_internal(slice in_msg) impure {
;; do nothing for internal messages ;; do nothing for internal messages
} }
_ days_passed() inline { _ seconds_passed(int start_at, int utime) inline_ref {
ifnot (start_at) {
var p = config_param(-13); var p = config_param(-13);
return null?(p) ? -1 : (now() - begin_parse(p).preload_uint(32)) / 86400; start_at = null?(p) ? 0 : begin_parse(p).preload_uint(32);
}
return start_at ? utime - start_at : -1;
} }
() recv_external(slice in_msg) impure { () recv_external(slice in_msg) impure {
@ -16,7 +19,7 @@ _ days_passed() inline {
var (msg_seqno, valid_until) = (cs~load_uint(32), cs~load_uint(32)); var (msg_seqno, valid_until) = (cs~load_uint(32), cs~load_uint(32));
throw_if(35, valid_until <= now()); throw_if(35, valid_until <= now());
var ds = get_data().begin_parse(); var ds = get_data().begin_parse();
var (stored_seqno, public_key, rdict) = (ds~load_uint(32), ds~load_uint(256), ds~load_dict()); var (stored_seqno, public_key, start_at, rdict) = (ds~load_uint(32), ds~load_uint(256), ds~load_uint(32), ds~load_dict());
ds.end_parse(); ds.end_parse();
throw_unless(33, msg_seqno == stored_seqno); throw_unless(33, msg_seqno == stored_seqno);
ifnot (msg_seqno) { ifnot (msg_seqno) {
@ -24,14 +27,15 @@ _ days_passed() inline {
set_data(begin_cell() set_data(begin_cell()
.store_uint(stored_seqno + 1, 32) .store_uint(stored_seqno + 1, 32)
.store_uint(public_key, 256) .store_uint(public_key, 256)
.store_uint(start_at, 32)
.store_dict(rdict) .store_dict(rdict)
.end_cell()); .end_cell());
return (); 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();
var ts = days_passed(); var ts = seconds_passed(start_at, now());
var (_, value, found) = rdict.idict_get_preveq?(16, ts); var (_, value, found) = rdict.idict_get_preveq?(32, ts);
if (found) { if (found) {
raw_reserve(value~load_grams(), 2); raw_reserve(value~load_grams(), 2);
} }
@ -45,6 +49,7 @@ _ days_passed() inline {
set_data(begin_cell() set_data(begin_cell()
.store_uint(stored_seqno + 1, 32) .store_uint(stored_seqno + 1, 32)
.store_uint(public_key, 256) .store_uint(public_key, 256)
.store_uint(start_at, 32)
.store_dict(rdict) .store_dict(rdict)
.end_cell()); .end_cell());
} }
@ -61,15 +66,23 @@ int get_public_key() method_id {
return cs.preload_uint(256); return cs.preload_uint(256);
} }
int balance() method_id { int compute_balance_at(int utime) inline_ref {
var ds = get_data().begin_parse().skip_bits(32 + 256); var ds = get_data().begin_parse().skip_bits(32 + 256);
var rdict = ds~load_dict(); var (start_at, rdict) = (ds~load_uint(32), ds~load_dict());
ds.end_parse(); ds.end_parse();
var ts = days_passed(); var ts = seconds_passed(start_at, utime);
var balance = get_balance().pair_first(); var balance = get_balance().pair_first();
var (_, value, found) = rdict.idict_get_preveq?(16, ts); var (_, value, found) = rdict.idict_get_preveq?(32, ts);
if (found) { if (found) {
balance = max(balance - value~load_grams(), 0); balance = max(balance - value~load_grams(), 0);
} }
return balance; return balance;
} }
int balance_at(int utime) method_id {
return compute_balance_at(utime);
}
int balance() method_id {
return compute_balance_at(now());
}

View file

@ -0,0 +1,103 @@
;; Restricted wallet initialized by a third party (a variant of restricted-wallet2-code.fc)
;; restricts access to parts of balance until certain dates
() recv_internal(slice in_msg) impure {
;; do nothing for internal messages
}
_ seconds_passed(int start_at, int utime) inline_ref {
ifnot (start_at) {
var p = config_param(-13);
start_at = null?(p) ? 0 : begin_parse(p).preload_uint(32);
}
return start_at ? utime - start_at : -1;
}
() recv_external(slice in_msg) impure {
var signature = in_msg~load_bits(512);
var cs = in_msg;
var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32));
throw_if(35, valid_until <= now());
var ds = get_data().begin_parse();
var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256));
throw_unless(33, msg_seqno == stored_seqno);
throw_unless(34, subwallet_id == stored_subwallet);
throw_unless(36, check_signature(slice_hash(in_msg), signature, public_key));
ifnot (msg_seqno) {
public_key = ds~load_uint(256); ;; load "final" public key
ds.end_parse();
cs~touch();
var (start_at, rdict) = (cs~load_uint(32), cs~load_dict());
cs.end_parse();
accept_message();
set_data(begin_cell()
.store_uint(stored_seqno + 1, 32)
.store_uint(stored_subwallet, 32)
.store_uint(public_key, 256)
.store_uint(start_at, 32)
.store_dict(rdict)
.end_cell());
return ();
}
var (start_at, rdict) = (ds~load_uint(32), ds~load_dict());
ds.end_parse();
accept_message();
var ts = seconds_passed(start_at, now());
var (_, value, found) = rdict.idict_get_preveq?(32, ts);
if (found) {
raw_reserve(value~load_grams(), 2);
}
cs~touch();
while (cs.slice_refs()) {
var mode = cs~load_uint(8);
var msg = cs~load_ref();
send_raw_message(msg, mode);
}
cs.end_parse();
set_data(begin_cell()
.store_uint(stored_seqno + 1, 32)
.store_uint(stored_subwallet, 32)
.store_uint(public_key, 256)
.store_uint(start_at, 32)
.store_dict(rdict)
.end_cell());
}
;; Get methods
int seqno() method_id {
return get_data().begin_parse().preload_uint(32);
}
int wallet_id() method_id {
var ds = get_data().begin_parse();
ds~load_uint(32);
return ds.preload_uint(32);
}
int get_public_key() method_id {
var ds = get_data().begin_parse();
ds~load_uint(32 + 32);
return ds.preload_uint(256);
}
int compute_balance_at(int utime) inline_ref {
var ds = get_data().begin_parse().skip_bits(32 + 32 + 256);
var (start_at, rdict) = (ds~load_uint(32), ds~load_dict());
ds.end_parse();
var ts = seconds_passed(start_at, utime);
var balance = get_balance().pair_first();
var (_, value, found) = rdict.idict_get_preveq?(32, ts);
if (found) {
balance = max(balance - value~load_grams(), 0);
}
return balance;
}
int balance_at(int utime) method_id {
return compute_balance_at(utime);
}
int balance() method_id {
return compute_balance_at(now());
}

View file

@ -18,5 +18,7 @@ dup Blen { 32 B>i@ } { drop Basechain } cond constant wallet_wc
file-base +".pk" dup file-exists? { file-base +".pk" dup file-exists? {
dup file>B dup Blen 32 <> abort"Private key must be exactly 32 bytes long" dup file>B dup Blen 32 <> abort"Private key must be exactly 32 bytes long"
=: wallet_pk ."Private key available in file " type cr tuck =: wallet_pk ."Private key available in file " type cr
priv>pub 256 B>u@
dup ."Corresponding public key is " .pubkey ." = " 64X. cr
} { ."Private key file " type ." not found" cr } cond } { ."Private key file " type ." not found" cr } cond

View file

@ -21,6 +21,31 @@
#include "block/block-auto.h" #include "block/block-auto.h"
#include "block/block-parse.h" #include "block/block-parse.h"
namespace ton { namespace ton {
namespace smc {
td::Ref<vm::CellSlice> pack_grams(td::uint64 amount) {
vm::CellBuilder cb;
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(amount));
return vm::load_cell_slice_ref(cb.finalize());
}
bool unpack_grams(td::Ref<vm::CellSlice> cs, td::uint64& amount) {
td::RefInt256 got;
if (!block::tlb::t_Grams.as_integer_to(cs, got)) {
return false;
}
if (!got->unsigned_fits_bits(63)) {
return false;
}
auto x = got->to_long();
if (x < 0) {
return false;
}
amount = x;
return true;
}
} // namespace smc
td::Ref<vm::Cell> GenericAccount::get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) noexcept { td::Ref<vm::Cell> GenericAccount::get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) noexcept {
return vm::CellBuilder() return vm::CellBuilder()
.store_zeroes(2) .store_zeroes(2)
@ -47,7 +72,7 @@ void GenericAccount::store_int_message(vm::CellBuilder& cb, const block::StdAddr
.store_long(dest_address.workchain, 8) .store_long(dest_address.workchain, 8)
.store_int256(dest_addr, 256); .store_int256(dest_addr, 256);
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms)); block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms));
cb.store_zeroes(9 + 64 + 32 + 1 + 1); cb.store_zeroes(9 + 64 + 32);
} }
td::Ref<vm::Cell> GenericAccount::create_ext_message(const block::StdAddress& address, td::Ref<vm::Cell> new_state, td::Ref<vm::Cell> GenericAccount::create_ext_message(const block::StdAddress& address, td::Ref<vm::Cell> new_state,

View file

@ -23,6 +23,10 @@
#include "SmartContract.h" #include "SmartContract.h"
namespace ton { namespace ton {
namespace smc {
td::Ref<vm::CellSlice> pack_grams(td::uint64 amount);
bool unpack_grams(td::Ref<vm::CellSlice> cs, td::uint64& amount);
} // namespace smc
class GenericAccount { class GenericAccount {
public: public:
static td::Ref<vm::Cell> get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) noexcept; static td::Ref<vm::Cell> get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) noexcept;

View file

@ -73,17 +73,11 @@ td::Ref<vm::Cell> HighloadWallet::make_a_gift_message(const td::Ed25519::Private
for (size_t i = 0; i < gifts.size(); i++) { for (size_t i = 0; i < gifts.size(); i++) {
auto& gift = gifts[i]; auto& gift = gifts[i];
td::int32 send_mode = 3; td::int32 send_mode = 3;
auto gramms = gift.gramms; if (gift.gramms == -1) {
if (gramms == -1) {
gramms = 0;
send_mode += 128; send_mode += 128;
} }
auto message_inner = create_int_message(gift);
vm::CellBuilder cb; vm::CellBuilder cb;
GenericAccount::store_int_message(cb, gift.destination, gramms);
cb.store_bytes("\0\0\0\0", 4);
vm::CellString::store(cb, gift.message, 35 * 8).ensure();
auto message_inner = cb.finalize();
cb = {};
cb.store_long(send_mode, 8).store_ref(message_inner); cb.store_long(send_mode, 8).store_ref(message_inner);
auto key = messages.integer_key(td::make_refint(i), 16, false); auto key = messages.integer_key(td::make_refint(i), 16, false);
messages.set_builder(key.bits(), 16, cb); messages.set_builder(key.bits(), 16, cb);

View file

@ -73,18 +73,11 @@ td::Ref<vm::Cell> HighloadWalletV2::make_a_gift_message(const td::Ed25519::Priva
for (size_t i = 0; i < gifts.size(); i++) { for (size_t i = 0; i < gifts.size(); i++) {
auto& gift = gifts[i]; auto& gift = gifts[i];
td::int32 send_mode = 3; td::int32 send_mode = 3;
auto gramms = gift.gramms; if (gift.gramms == -1) {
if (gramms == -1) {
gramms = 0;
send_mode += 128; send_mode += 128;
} }
vm::CellBuilder cb; vm::CellBuilder cb;
GenericAccount::store_int_message(cb, gift.destination, gramms); cb.store_long(send_mode, 8).store_ref(create_int_message(gift));
cb.store_bytes("\0\0\0\0", 4);
vm::CellString::store(cb, gift.message, 35 * 8).ensure();
auto message_inner = cb.finalize();
cb = {};
cb.store_long(send_mode, 8).store_ref(message_inner);
auto key = messages.integer_key(td::make_refint(i), 16, false); auto key = messages.integer_key(td::make_refint(i), 16, false);
messages.set_builder(key.bits(), 16, cb); messages.set_builder(key.bits(), 16, cb);
} }

View file

@ -0,0 +1,264 @@
#include "PaymentChannel.h"
#include "GenericAccount.h"
#include "vm/cells.h"
#include "vm/cellslice.h"
#include "Ed25519.h"
#include "block/block-auto.h"
#include "block/block-parse.h"
#include "SmartContract.h"
#include "SmartContractCode.h"
namespace ton {
using smc::pack_grams;
using smc::unpack_grams;
namespace pchan {
td::Ref<vm::Cell> Config::serialize() const {
block::gen::ChanConfig::Record rec;
vm::CellBuilder a_addr_cb;
block::tlb::t_MsgAddressInt.store_std_address(a_addr_cb, a_addr);
rec.a_addr = a_addr_cb.finalize_novm();
vm::CellBuilder b_addr_cb;
block::tlb::t_MsgAddressInt.store_std_address(b_addr_cb, b_addr);
rec.b_addr = b_addr_cb.finalize_novm();
rec.a_key.as_slice().copy_from(a_key);
rec.b_key.as_slice().copy_from(b_key);
rec.init_timeout = init_timeout;
rec.close_timeout = close_timeout;
rec.channel_id = channel_id;
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
td::Ref<vm::Cell> MsgInit::serialize() const {
block::gen::ChanMsg::Record_chan_msg_init rec;
rec.min_A = pack_grams(min_A);
rec.min_B = pack_grams(min_B);
rec.inc_A = pack_grams(inc_A);
rec.inc_B = pack_grams(inc_B);
rec.channel_id = channel_id;
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
td::Ref<vm::Cell> Promise::serialize() const {
block::gen::ChanPromise::Record rec;
rec.channel_id = channel_id;
rec.promise_A = pack_grams(promise_A);
rec.promise_B = pack_grams(promise_B);
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
td::SecureString sign(const td::Ref<vm::Cell>& msg, const td::Ed25519::PrivateKey* key) {
return key->sign(msg->get_hash().as_slice()).move_as_ok();
}
td::Ref<vm::Cell> maybe_sign(const td::Ref<vm::Cell>& msg, const td::Ed25519::PrivateKey* key) {
if (!key) {
return {};
}
return vm::CellBuilder().store_bytes(sign(msg, key).as_slice()).finalize();
}
td::Ref<vm::CellSlice> maybe_ref(td::Ref<vm::Cell> msg) {
vm::CellBuilder cb;
CHECK(cb.store_maybe_ref(msg));
return vm::load_cell_slice_ref(cb.finalize());
}
td::Ref<vm::Cell> MsgClose::serialize() const {
block::gen::ChanMsg::Record_chan_msg_close rec;
rec.extra_A = pack_grams(extra_A);
rec.extra_B = pack_grams(extra_B);
rec.promise = signed_promise;
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
td::Ref<vm::Cell> MsgTimeout::serialize() const {
block::gen::ChanMsg::Record_chan_msg_timeout rec;
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
td::SecureString SignedPromise::signature(const td::Ed25519::PrivateKey* key, const td::Ref<vm::Cell>& promise) {
return sign(promise, key);
}
td::Ref<vm::Cell> SignedPromise::create_and_serialize(td::Slice signature, const td::Ref<vm::Cell>& promise) {
block::gen::ChanSignedPromise::Record rec;
rec.promise = vm::load_cell_slice_ref(promise);
LOG(ERROR) << "signature.size() = " << signature.size();
rec.sig = maybe_ref(vm::CellBuilder().store_bytes(signature).finalize());
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
td::Ref<vm::Cell> SignedPromise::create_and_serialize(const td::Ed25519::PrivateKey* key,
const td::Ref<vm::Cell>& promise) {
block::gen::ChanSignedPromise::Record rec;
rec.promise = vm::load_cell_slice_ref(promise);
rec.sig = maybe_ref(maybe_sign(promise, key));
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
bool SignedPromise::unpack(td::Ref<vm::Cell> cell) {
block::gen::ChanSignedPromise::Record rec;
if (!tlb::unpack_cell(cell, rec)) {
return false;
}
block::gen::ChanPromise::Record rec_promise;
if (!tlb::csr_unpack(rec.promise, rec_promise)) {
return false;
}
promise.channel_id = rec_promise.channel_id;
if (!unpack_grams(rec_promise.promise_A, promise.promise_A)) {
return false;
}
if (!unpack_grams(rec_promise.promise_B, promise.promise_B)) {
return false;
}
td::Ref<vm::Cell> sig_cell;
if (!rec.sig->prefetch_maybe_ref(sig_cell)) {
return false;
}
td::SecureString signature(64);
vm::CellSlice cs = vm::load_cell_slice(sig_cell);
if (!cs.prefetch_bytes(signature.as_mutable_slice())) {
return false;
}
o_signature = std::move(signature);
return true;
}
td::Ref<vm::Cell> StateInit::serialize() const {
block::gen::ChanState::Record_chan_state_init rec;
rec.expire_at = expire_at;
rec.min_A = pack_grams(min_A);
rec.min_B = pack_grams(min_B);
rec.A = pack_grams(A);
rec.B = pack_grams(B);
rec.signed_A = signed_A;
rec.signed_B = signed_B;
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
td::Ref<vm::Cell> Data::serialize() const {
block::gen::ChanData::Record rec;
rec.config = config;
rec.state = state;
td::Ref<vm::Cell> res;
CHECK(block::gen::t_ChanData.cell_pack(res, rec));
return res;
}
td::Ref<vm::Cell> Data::init_state() {
return StateInit().serialize();
}
} // namespace pchan
td::Result<PaymentChannel::Info> PaymentChannel::get_info() const {
block::gen::ChanData::Record data_rec;
if (!tlb::unpack_cell(get_state().data, data_rec)) {
return td::Status::Error("Can't unpack data");
}
block::gen::ChanConfig::Record config_rec;
if (!tlb::unpack_cell(data_rec.config, config_rec)) {
return td::Status::Error("Can't unpack config");
}
pchan::Config config;
config.a_key = td::SecureString(config_rec.a_key.as_slice());
config.b_key = td::SecureString(config_rec.b_key.as_slice());
block::tlb::t_MsgAddressInt.extract_std_address(vm::load_cell_slice_ref(config_rec.a_addr), config.a_addr);
block::tlb::t_MsgAddressInt.extract_std_address(vm::load_cell_slice_ref(config_rec.b_addr), config.b_addr);
config.init_timeout = static_cast<td::int32>(config_rec.init_timeout);
config.close_timeout = static_cast<td::int32>(config_rec.close_timeout);
config.channel_id = static_cast<td::int64>(config_rec.channel_id);
auto state_cs = vm::load_cell_slice(data_rec.state);
Info res;
switch (block::gen::t_ChanState.check_tag(state_cs)) {
case block::gen::ChanState::chan_state_init: {
pchan::StateInit state;
block::gen::ChanState::Record_chan_state_init state_rec;
if (!tlb::unpack_cell(data_rec.state, state_rec)) {
return td::Status::Error("Can't unpack state");
}
bool ok = unpack_grams(state_rec.A, state.A) && unpack_grams(state_rec.B, state.B) &&
unpack_grams(state_rec.min_A, state.min_A) && unpack_grams(state_rec.min_B, state.min_B);
state.expire_at = state_rec.expire_at;
state.signed_A = state_rec.signed_A;
state.signed_B = state_rec.signed_B;
if (!ok) {
return td::Status::Error("Can't unpack state");
}
res.state = std::move(state);
break;
}
case block::gen::ChanState::chan_state_close: {
pchan::StateClose state;
block::gen::ChanState::Record_chan_state_close state_rec;
if (!tlb::unpack_cell(data_rec.state, state_rec)) {
return td::Status::Error("Can't unpack state");
}
bool ok = unpack_grams(state_rec.A, state.A) && unpack_grams(state_rec.B, state.B) &&
unpack_grams(state_rec.promise_A, state.promise_A) &&
unpack_grams(state_rec.promise_B, state.promise_B);
state.expire_at = state_rec.expire_at;
state.signed_A = state_rec.signed_A;
state.signed_B = state_rec.signed_B;
if (!ok) {
return td::Status::Error("Can't unpack state");
}
res.state = std::move(state);
break;
}
case block::gen::ChanState::chan_state_payout: {
pchan::StatePayout state;
block::gen::ChanState::Record_chan_state_payout state_rec;
if (!tlb::unpack_cell(data_rec.state, state_rec)) {
return td::Status::Error("Can't unpack state");
}
bool ok = unpack_grams(state_rec.A, state.A) && unpack_grams(state_rec.B, state.B);
if (!ok) {
return td::Status::Error("Can't unpack state");
}
res.state = std::move(state);
break;
}
default:
return td::Status::Error("Can't unpack state");
}
res.config = std::move(config);
res.description = block::gen::t_ChanState.as_string_ref(data_rec.state);
return std::move(res);
} // namespace ton
td::optional<td::int32> PaymentChannel::guess_revision(const vm::Cell::Hash& code_hash) {
for (auto i : ton::SmartContractCode::get_revisions(ton::SmartContractCode::PaymentChannel)) {
auto code = SmartContractCode::get_code(SmartContractCode::PaymentChannel, i);
if (code->get_hash() == code_hash) {
return i;
}
}
return {};
}
} // namespace ton

View file

@ -0,0 +1,251 @@
#pragma once
#include "vm/cells.h"
#include "vm/cellslice.h"
#include "Ed25519.h"
#include "block/block-auto.h"
#include "block/block-parse.h"
#include "td/utils/Variant.h"
#include "SmartContract.h"
#include "SmartContractCode.h"
namespace ton {
namespace pchan {
//
// Payment channels
//
struct Config {
td::uint32 init_timeout{0};
td::uint32 close_timeout{0};
td::SecureString a_key;
td::SecureString b_key;
block::StdAddress a_addr;
block::StdAddress b_addr;
td::uint64 channel_id{0};
td::Ref<vm::Cell> serialize() const;
};
struct MsgInit {
td::uint64 inc_A{0};
td::uint64 inc_B{0};
td::uint64 min_A{0};
td::uint64 min_B{0};
td::uint64 channel_id{0};
td::Ref<vm::Cell> serialize() const;
};
struct Promise {
td::uint64 channel_id;
td::uint64 promise_A{0};
td::uint64 promise_B{0};
td::Ref<vm::Cell> serialize() const;
};
td::Ref<vm::Cell> maybe_sign(const td::Ref<vm::Cell>& msg, const td::Ed25519::PrivateKey* key);
td::Ref<vm::CellSlice> maybe_ref(td::Ref<vm::Cell> msg);
struct MsgClose {
td::uint64 extra_A{0};
td::uint64 extra_B{0};
td::Ref<vm::CellSlice> signed_promise;
td::Ref<vm::Cell> serialize() const;
};
struct MsgTimeout {
td::Ref<vm::Cell> serialize() const;
};
struct SignedPromise {
Promise promise;
td::optional<td::SecureString> o_signature;
bool unpack(td::Ref<vm::Cell> cell);
static td::SecureString signature(const td::Ed25519::PrivateKey* key, const td::Ref<vm::Cell>& promise);
static td::Ref<vm::Cell> create_and_serialize(td::Slice signature, const td::Ref<vm::Cell>& promise);
static td::Ref<vm::Cell> create_and_serialize(const td::Ed25519::PrivateKey* key, const td::Ref<vm::Cell>& promise);
};
struct StateInit {
bool signed_A{false};
bool signed_B{false};
td::uint64 min_A{0};
td::uint64 min_B{0};
td::uint64 A{0};
td::uint64 B{0};
td::uint32 expire_at{0};
td::Ref<vm::Cell> serialize() const;
};
struct StateClose {
bool signed_A{false};
bool signed_B{false};
td::uint64 promise_A{0};
td::uint64 promise_B{0};
td::uint64 A{0};
td::uint64 B{0};
td::uint32 expire_at{0};
};
struct StatePayout {
td::uint64 A{0};
td::uint64 B{0};
};
struct Data {
td::Ref<vm::Cell> config;
td::Ref<vm::Cell> state;
static td::Ref<vm::Cell> init_state();
td::Ref<vm::Cell> serialize() const;
};
template <class T>
struct MsgBuilder {
td::Ed25519::PrivateKey* a_key{nullptr};
td::Ed25519::PrivateKey* b_key{nullptr};
T&& with_a_key(td::Ed25519::PrivateKey* key) && {
a_key = key;
return static_cast<T&&>(*this);
}
T&& with_b_key(td::Ed25519::PrivateKey* key) && {
b_key = key;
return static_cast<T&&>(*this);
}
td::Ref<vm::Cell> finalize() && {
block::gen::ChanSignedMsg::Record rec;
auto msg = static_cast<T&&>(*this).msg.serialize();
rec.msg = vm::load_cell_slice_ref(msg);
rec.sig_A = maybe_ref(maybe_sign(msg, a_key));
rec.sig_B = maybe_ref(maybe_sign(msg, b_key));
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
};
struct MsgInitBuilder : public MsgBuilder<MsgInitBuilder> {
MsgInit msg;
MsgInitBuilder&& min_A(td::uint64 value) && {
msg.min_A = value;
return std::move(*this);
}
MsgInitBuilder&& min_B(td::uint64 value) && {
msg.min_B = value;
return std::move(*this);
}
MsgInitBuilder&& inc_A(td::uint64 value) && {
msg.inc_A = value;
return std::move(*this);
}
MsgInitBuilder&& inc_B(td::uint64 value) && {
msg.inc_B = value;
return std::move(*this);
}
MsgInitBuilder&& channel_id(td::uint64 value) && {
msg.channel_id = value;
return std::move(*this);
}
};
struct MsgTimeoutBuilder : public MsgBuilder<MsgTimeoutBuilder> {
MsgTimeout msg;
};
struct MsgCloseBuilder : public MsgBuilder<MsgCloseBuilder> {
MsgClose msg;
MsgCloseBuilder&& extra_A(td::uint64 value) && {
msg.extra_A = value;
return std::move(*this);
}
MsgCloseBuilder&& extra_B(td::uint64 value) && {
msg.extra_B = value;
return std::move(*this);
}
MsgCloseBuilder&& signed_promise(td::Ref<vm::Cell> signed_promise) && {
msg.signed_promise = vm::load_cell_slice_ref(signed_promise);
return std::move(*this);
}
};
struct SignedPromiseBuilder {
Promise promise;
td::optional<td::SecureString> o_signature;
td::Ed25519::PrivateKey* key{nullptr};
SignedPromiseBuilder& with_key(td::Ed25519::PrivateKey* key) {
this->key = key;
return *this;
}
SignedPromiseBuilder& promise_A(td::uint64 value) {
promise.promise_A = value;
return *this;
}
SignedPromiseBuilder& promise_B(td::uint64 value) {
promise.promise_B = value;
return *this;
}
SignedPromiseBuilder& channel_id(td::uint64 value) {
promise.channel_id = value;
return *this;
}
SignedPromiseBuilder& signature(td::SecureString signature) {
o_signature = std::move(signature);
return *this;
}
bool check_signature(td::Slice signature, const td::Ed25519::PublicKey& pk) {
return pk.verify_signature(promise.serialize()->get_hash().as_slice(), signature).is_ok();
}
td::SecureString calc_signature() {
CHECK(key);
return SignedPromise::signature(key, promise.serialize());
}
td::Ref<vm::Cell> finalize() {
if (o_signature) {
return SignedPromise::create_and_serialize(o_signature.value().copy(), promise.serialize());
} else {
return SignedPromise::create_and_serialize(key, promise.serialize());
}
}
};
} // namespace pchan
class PaymentChannel : public SmartContract {
public:
PaymentChannel(State state) : SmartContract(std::move(state)) {
}
struct Info {
pchan::Config config;
td::Variant<pchan::StateInit, pchan::StateClose, pchan::StatePayout> state;
std::string description;
};
td::Result<Info> get_info() const;
static td::Ref<PaymentChannel> create(State state) {
return td::Ref<PaymentChannel>(true, std::move(state));
}
static td::optional<td::int32> guess_revision(const vm::Cell::Hash& code_hash);
static td::Ref<PaymentChannel> create(const pchan::Config& config, td::int32 revision) {
State state;
state.code = SmartContractCode::get_code(SmartContractCode::PaymentChannel, revision);
pchan::Data data;
data.config = config.serialize();
pchan::StateInit init;
data.state = init.serialize();
state.data = data.serialize();
return create(std::move(state));
}
};
} // namespace ton

View file

@ -32,19 +32,19 @@
namespace ton { namespace ton {
namespace { namespace {
td::Ref<vm::Stack> prepare_vm_stack(td::Ref<vm::CellSlice> body) { td::Ref<vm::Stack> prepare_vm_stack(td::RefInt256 amount, td::Ref<vm::CellSlice> body) {
td::Ref<vm::Stack> stack_ref{true}; td::Ref<vm::Stack> stack_ref{true};
td::RefInt256 acc_addr{true}; td::RefInt256 acc_addr{true};
//CHECK(acc_addr.write().import_bits(account.addr.cbits(), 256)); //CHECK(acc_addr.write().import_bits(account.addr.cbits(), 256));
vm::Stack& stack = stack_ref.write(); vm::Stack& stack = stack_ref.write();
stack.push_int(td::make_refint(10000000000)); stack.push_int(td::make_refint(10000000000));
stack.push_int(td::make_refint(10000000000)); stack.push_int(std::move(amount));
stack.push_cell(vm::CellBuilder().finalize()); stack.push_cell(vm::CellBuilder().finalize());
stack.push_cellslice(std::move(body)); stack.push_cellslice(std::move(body));
return stack_ref; return stack_ref;
} }
td::Ref<vm::Tuple> prepare_vm_c7(td::uint32 now) { td::Ref<vm::Tuple> prepare_vm_c7(td::uint32 now, td::uint64 balance) {
// TODO: fix initialization of c7 // TODO: fix initialization of c7
td::BitArray<256> rand_seed; td::BitArray<256> rand_seed;
rand_seed.as_slice().fill(0); rand_seed.as_slice().fill(0);
@ -58,7 +58,7 @@ td::Ref<vm::Tuple> prepare_vm_c7(td::uint32 now) {
td::make_refint(0), // block_lt:Integer td::make_refint(0), // block_lt:Integer
td::make_refint(0), // trans_lt:Integer td::make_refint(0), // trans_lt:Integer
std::move(rand_seed_int), // rand_seed:Integer std::move(rand_seed_int), // rand_seed:Integer
block::CurrencyCollection(1000000000).as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)] block::CurrencyCollection(balance).as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)]
vm::load_cell_slice_ref(vm::CellBuilder().finalize()) // myself:MsgAddressInt vm::load_cell_slice_ref(vm::CellBuilder().finalize()) // myself:MsgAddressInt
//vm::StackEntry::maybe(td::Ref<vm::Cell>()) //vm::StackEntry::maybe(td::Ref<vm::Cell>())
); // global_config:(Maybe Cell) ] = SmartContractInfo; ); // global_config:(Maybe Cell) ] = SmartContractInfo;
@ -66,6 +66,15 @@ td::Ref<vm::Tuple> prepare_vm_c7(td::uint32 now) {
return vm::make_tuple_ref(std::move(tuple)); return vm::make_tuple_ref(std::move(tuple));
} }
static int output_actions_count(td::Ref<vm::Cell> list) {
int i = -1;
do {
++i;
list = load_cell_slice(std::move(list)).prefetch_ref();
} while (list.not_null());
return i;
}
SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stack> stack, td::Ref<vm::Tuple> c7, SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stack> stack, td::Ref<vm::Tuple> c7,
vm::GasLimits gas, bool ignore_chksig) { vm::GasLimits gas, bool ignore_chksig) {
auto gas_credit = gas.gas_credit; auto gas_credit = gas.gas_credit;
@ -123,6 +132,8 @@ SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stac
if (res.success) { if (res.success) {
res.new_state.data = vm.get_c4(); res.new_state.data = vm.get_c4();
res.actions = vm.get_d(5); res.actions = vm.get_d(5);
LOG(DEBUG) << "output actions:\n"
<< block::gen::OutList{output_actions_count(res.actions)}.as_string_ref(res.actions);
} }
LOG_IF(ERROR, gas_credit != 0 && (res.accepted && !res.success)) LOG_IF(ERROR, gas_credit != 0 && (res.accepted && !res.success))
<< "Accepted but failed with code " << res.code << "\n" << "Accepted but failed with code " << res.code << "\n"
@ -171,10 +182,13 @@ SmartContract::Answer SmartContract::run_method(Args args) {
now = args.now.unwrap(); now = args.now.unwrap();
} }
if (!args.c7) { if (!args.c7) {
args.c7 = prepare_vm_c7(now); args.c7 = prepare_vm_c7(now, args.balance);
} }
if (!args.limits) { if (!args.limits) {
args.limits = vm::GasLimits{(long long)0, (long long)1000000, (long long)10000}; bool is_internal = args.get_method_id().ok() == 0;
args.limits = vm::GasLimits{is_internal ? (long long)args.amount * 1000 : (long long)0, (long long)1000000,
is_internal ? 0 : (long long)10000};
} }
CHECK(args.stack); CHECK(args.stack);
CHECK(args.method_id); CHECK(args.method_id);
@ -191,7 +205,7 @@ SmartContract::Answer SmartContract::run_get_method(Args args) const {
now = args.now.unwrap(); now = args.now.unwrap();
} }
if (!args.c7) { if (!args.c7) {
args.c7 = prepare_vm_c7(now); args.c7 = prepare_vm_c7(now, args.balance);
} }
if (!args.limits) { if (!args.limits) {
args.limits = vm::GasLimits{1000000}; args.limits = vm::GasLimits{1000000};
@ -209,6 +223,11 @@ SmartContract::Answer SmartContract::run_get_method(td::Slice method, Args args)
} }
SmartContract::Answer SmartContract::send_external_message(td::Ref<vm::Cell> cell, Args args) { 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)); return run_method(
args.set_stack(prepare_vm_stack(td::make_refint(0), vm::load_cell_slice_ref(cell))).set_method_id(-1));
}
SmartContract::Answer SmartContract::send_internal_message(td::Ref<vm::Cell> cell, Args args) {
return run_method(
args.set_stack(prepare_vm_stack(td::make_refint(args.amount), vm::load_cell_slice_ref(cell))).set_method_id(0));
} }
} // namespace ton } // namespace ton

View file

@ -57,6 +57,8 @@ class SmartContract : public td::CntObject {
td::optional<td::Ref<vm::Stack>> stack; td::optional<td::Ref<vm::Stack>> stack;
td::optional<td::int32> now; td::optional<td::int32> now;
bool ignore_chksig{false}; bool ignore_chksig{false};
td::uint64 amount{0};
td::uint64 balance{0};
Args() { Args() {
} }
@ -95,6 +97,14 @@ class SmartContract : public td::CntObject {
this->ignore_chksig = ignore_chksig; this->ignore_chksig = ignore_chksig;
return std::move(*this); return std::move(*this);
} }
Args&& set_amount(td::uint64 amount) {
this->amount = amount;
return std::move(*this);
}
Args&& set_balance(td::uint64 balance) {
this->balance = balance;
return std::move(*this);
}
td::Result<td::int32> get_method_id() const { td::Result<td::int32> get_method_id() const {
if (!method_id) { if (!method_id) {
@ -109,6 +119,7 @@ class SmartContract : public td::CntObject {
Answer run_get_method(Args args = {}) const; Answer run_get_method(Args args = {}) const;
Answer run_get_method(td::Slice 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 = {}); Answer send_external_message(td::Ref<vm::Cell> cell, Args args = {});
Answer send_internal_message(td::Ref<vm::Cell> cell, Args args = {});
size_t code_size() const; size_t code_size() const;
size_t data_size() const; size_t data_size() const;
@ -122,6 +133,9 @@ class SmartContract : public td::CntObject {
const State& get_state() const { const State& get_state() const {
return state_; return state_;
} }
CntObject* make_copy() const override {
return new SmartContract(state_);
}
protected: protected:
State state_; State state_;

View file

@ -44,6 +44,8 @@ const auto& get_map() {
#include "smartcont/auto/highload-wallet-code.cpp" #include "smartcont/auto/highload-wallet-code.cpp"
#include "smartcont/auto/highload-wallet-v2-code.cpp" #include "smartcont/auto/highload-wallet-v2-code.cpp"
#include "smartcont/auto/dns-manual-code.cpp" #include "smartcont/auto/dns-manual-code.cpp"
#include "smartcont/auto/payment-channel-code.cpp"
#include "smartcont/auto/restricted-wallet3-code.cpp"
with_tvm_code("highload-wallet-r1", with_tvm_code("highload-wallet-r1",
"te6ccgEBBgEAhgABFP8A9KQT9KDyyAsBAgEgAgMCAUgEBQC88oMI1xgg0x/TH9Mf+CMTu/Jj7UTQ0x/TH9P/" "te6ccgEBBgEAhgABFP8A9KQT9KDyyAsBAgEgAgMCAUgEBQC88oMI1xgg0x/TH9Mf+CMTu/Jj7UTQ0x/TH9P/"
@ -96,6 +98,14 @@ const auto& get_map() {
"FwCEMQLTAAHAAZPUAdCY0wUBqgLXGAHiINdJwg/" "FwCEMQLTAAHAAZPUAdCY0wUBqgLXGAHiINdJwg/"
"ypiB41yLXCwfyaHBTEddJqTYCmNMHAcAAEqEB5DDIywYBzxbJ0FADACBZ9KhvpSCUAvQEMJIybeICACg0A4AQ9FqZECOECUBE8AEBkjAx4gBmM" "ypiB41yLXCwfyaHBTEddJqTYCmNMHAcAAEqEB5DDIywYBzxbJ0FADACBZ9KhvpSCUAvQEMJIybeICACg0A4AQ9FqZECOECUBE8AEBkjAx4gBmM"
"SLAFZwy9AQQI4QJUELwAQHgIsAWmDIChAn0czAB4DAyIMAfkzD0BODAIJJtAeDyLG0B"); "SLAFZwy9AQQI4QJUELwAQHgIsAWmDIChAn0czAB4DAyIMAfkzD0BODAIJJtAeDyLG0B");
with_tvm_code(
"restricted-wallet3-r1",
"te6ccgECEgEAAUsAART/APSkE/S88sgLAQIBIAIDAgFIBAUD+PKDCNcYINMf0x/THwL4I7vyY+1E0NMf0x/T/"
"1NDuvKhUWK68qIG+QFUEHb5EPKkAY4fMwHT/9EB0x/0BNH4AAOkyMsfFMsfy/8Syx/0AMntVOEC0x/"
"0BNH4ACH4I9s8IYAg9HtvpTGW+gAwcvsCkTDiApMg10qK6NECpMgPEBEABNAwAgEgBgcCASAICQIBSAwNAgFuCgsAEbjJftRNDXCx+"
"AAXrc52omhpn5jrhf/AABesePaiaGmPmOuFj8ABDbbYHwR7Z5AOAQm1B1tnkA4BTu1E0IEBQNch0x/"
"0BNEC2zz4J28QAoAg9HtvpTGX+gAwoXC2CZEw4g8AOiGOETGA8/gzIG6SMHCU0NcLH+IB3yGSAaGSW3/iAAzTB9QC+wAAHssfFMsfEsv/yx/"
"0AMntVA==");
return map; return map;
}(); }();
return map; return map;
@ -103,7 +113,6 @@ const auto& get_map() {
} // namespace } // namespace
td::Result<td::Ref<vm::Cell>> SmartContractCode::load(td::Slice name) { td::Result<td::Ref<vm::Cell>> SmartContractCode::load(td::Slice name) {
LOG(ERROR) << "LOAD " << name;
auto& map = get_map(); auto& map = get_map();
auto it = map.find(name); auto it = map.find(name);
if (it == map.end()) { if (it == map.end()) {
@ -146,6 +155,14 @@ td::Span<int> SmartContractCode::get_revisions(Type type) {
static int res[] = {-1, 1}; static int res[] = {-1, 1};
return res; return res;
} }
case Type::PaymentChannel: {
static int res[] = {-1};
return res;
}
case Type::RestrictedWallet: {
static int res[] = {-1, 1};
return res;
}
} }
UNREACHABLE(); UNREACHABLE();
return {}; return {};
@ -190,6 +207,10 @@ td::Ref<vm::Cell> SmartContractCode::get_code(Type type, int ext_revision) {
return "multisig"; return "multisig";
case Type::ManualDns: case Type::ManualDns:
return "dns-manual"; return "dns-manual";
case Type::PaymentChannel:
return "payment-channel";
case Type::RestrictedWallet:
return "restricted-wallet3";
} }
UNREACHABLE(); UNREACHABLE();
return ""; return "";

View file

@ -16,6 +16,7 @@
Copyright 2017-2020 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#pragma once
#include "vm/cells.h" #include "vm/cells.h"
#include "td/utils/Span.h" #include "td/utils/Span.h"
@ -25,7 +26,18 @@ class SmartContractCode {
public: public:
static td::Result<td::Ref<vm::Cell>> load(td::Slice name); static td::Result<td::Ref<vm::Cell>> load(td::Slice name);
enum Type { WalletV1 = 1, WalletV1Ext, WalletV2, WalletV3, HighloadWalletV1, HighloadWalletV2, ManualDns, Multisig }; enum Type {
WalletV1 = 1,
WalletV1Ext,
WalletV2,
WalletV3,
HighloadWalletV1,
HighloadWalletV2,
ManualDns,
Multisig,
PaymentChannel,
RestrictedWallet
};
static td::Span<int> get_revisions(Type type); static td::Span<int> get_revisions(Type type);
static td::Result<int> validate_revision(Type type, int revision); static td::Result<int> validate_revision(Type type, int revision);
static td::Ref<vm::Cell> get_code(Type type, int revision = 0); static td::Ref<vm::Cell> get_code(Type type, int revision = 0);

View file

@ -43,12 +43,7 @@ td::Ref<vm::Cell> TestGiver::make_a_gift_message_static(td::uint32 seqno, td::Sp
for (auto& gift : gifts) { for (auto& gift : gifts) {
td::int32 send_mode = 1; td::int32 send_mode = 1;
auto gramms = gift.gramms; cb.store_long(send_mode, 8).store_ref(create_int_message(gift));
vm::CellBuilder cbi;
GenericAccount::store_int_message(cbi, gift.destination, gramms);
store_gift_message(cbi, gift);
auto message_inner = cbi.finalize();
cb.store_long(send_mode, 8).store_ref(std::move(message_inner));
} }
return cb.finalize(); return cb.finalize();

View file

@ -46,18 +46,11 @@ td::Ref<vm::Cell> TestWallet::make_a_gift_message_static(const td::Ed25519::Priv
for (auto& gift : gifts) { for (auto& gift : gifts) {
td::int32 send_mode = 3; td::int32 send_mode = 3;
auto gramms = gift.gramms; if (gift.gramms == -1) {
if (gramms == -1) {
gramms = 0;
send_mode += 128; send_mode += 128;
} }
vm::CellBuilder cbi; cb.store_long(send_mode, 8).store_ref(create_int_message(gift));
GenericAccount::store_int_message(cbi, gift.destination, gramms);
store_gift_message(cbi, gift);
auto message_inner = cbi.finalize();
cb.store_long(send_mode, 8).store_ref(std::move(message_inner));
} }
auto message_outer = cb.finalize(); auto message_outer = cb.finalize();
auto signature = private_key.sign(message_outer->get_hash().as_slice()).move_as_ok(); 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(); return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();

View file

@ -52,16 +52,10 @@ td::Ref<vm::Cell> Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& pri
for (auto& gift : gifts) { for (auto& gift : gifts) {
td::int32 send_mode = 3; td::int32 send_mode = 3;
auto gramms = gift.gramms; if (gift.gramms == -1) {
if (gramms == -1) {
gramms = 0;
send_mode += 128; send_mode += 128;
} }
vm::CellBuilder cbi; cb.store_long(send_mode, 8).store_ref(create_int_message(gift));
GenericAccount::store_int_message(cbi, gift.destination, gramms);
store_gift_message(cbi, gift);
auto message_inner = cbi.finalize();
cb.store_long(send_mode, 8).store_ref(std::move(message_inner));
} }
auto message_outer = cb.finalize(); auto message_outer = cb.finalize();

View file

@ -24,6 +24,7 @@
#include "vm/cells/CellString.h" #include "vm/cells/CellString.h"
#include "SmartContract.h" #include "SmartContract.h"
#include "GenericAccount.h"
namespace ton { namespace ton {
class WalletInterface { class WalletInterface {
@ -36,6 +37,7 @@ class WalletInterface {
std::string message; std::string message;
td::Ref<vm::Cell> body; td::Ref<vm::Cell> body;
td::Ref<vm::Cell> init_state;
}; };
virtual ~WalletInterface() { virtual ~WalletInterface() {
@ -48,15 +50,29 @@ class WalletInterface {
return td::Status::Error("Unsupported"); return td::Status::Error("Unsupported");
} }
td::Result<td::Ref<vm::Cell>> get_init_message(const td::Ed25519::PrivateKey &private_key, td::Result<td::Ref<vm::Cell>> get_init_message(
td::uint32 valid_until = std::numeric_limits<td::uint32>::max()) { const td::Ed25519::PrivateKey &private_key,
td::uint32 valid_until = std::numeric_limits<td::uint32>::max()) const {
return make_a_gift_message(private_key, valid_until, {}); return make_a_gift_message(private_key, valid_until, {});
} }
static td::Ref<vm::Cell> create_int_message(const Gift &gift) {
vm::CellBuilder cbi;
GenericAccount::store_int_message(cbi, gift.destination, gift.gramms < 0 ? 0 : gift.gramms);
if (gift.init_state.not_null()) {
cbi.store_ones(2);
cbi.store_ref(gift.init_state);
} else {
cbi.store_zeroes(1);
}
cbi.store_zeroes(1);
store_gift_message(cbi, gift);
return cbi.finalize();
}
static void store_gift_message(vm::CellBuilder &cb, const Gift &gift) { static void store_gift_message(vm::CellBuilder &cb, const Gift &gift) {
if (gift.body.not_null()) { if (gift.body.not_null()) {
auto body = vm::load_cell_slice(gift.body); auto body = vm::load_cell_slice(gift.body);
//TODO: handle error //TODO: handle error
cb.append_cellslice_bool(body); CHECK(cb.append_cellslice_bool(body));
return; return;
} }

View file

@ -62,16 +62,10 @@ td::Ref<vm::Cell> WalletV3::make_a_gift_message(const td::Ed25519::PrivateKey& p
for (auto& gift : gifts) { for (auto& gift : gifts) {
td::int32 send_mode = 3; td::int32 send_mode = 3;
auto gramms = gift.gramms; if (gift.gramms == -1) {
if (gramms == -1) {
gramms = 0;
send_mode += 128; send_mode += 128;
} }
vm::CellBuilder cbi; cb.store_long(send_mode, 8).store_ref(create_int_message(gift));
GenericAccount::store_int_message(cbi, gift.destination, gramms);
store_gift_message(cbi, gift);
auto message_inner = cbi.finalize();
cb.store_long(send_mode, 8).store_ref(std::move(message_inner));
} }
auto message_outer = cb.finalize(); auto message_outer = cb.finalize();

View file

@ -70,3 +70,209 @@ class WalletV3 : public ton::SmartContract, public WalletInterface {
td::Result<td::Ed25519::PublicKey> get_public_key_or_throw() const; td::Result<td::Ed25519::PublicKey> get_public_key_or_throw() const;
}; };
} // namespace ton } // namespace ton
#include "smc-envelope/SmartContractCode.h"
#include "smc-envelope/GenericAccount.h"
#include "block/block-parse.h"
#include <algorithm>
namespace ton {
template <class WalletT, class TraitsT>
class WalletBase : public SmartContract, public WalletInterface {
public:
using Traits = TraitsT;
using InitData = typename Traits::InitData;
explicit WalletBase(State state) : SmartContract(std::move(state)) {
}
static td::Ref<WalletT> create(State state) {
return td::Ref<WalletT>(true, std::move(state));
}
static td::Ref<vm::Cell> get_init_code(int revision) {
return SmartContractCode::get_code(get_code_type(), revision);
};
size_t get_max_gifts_size() const override {
return Traits::max_gifts_size;
}
static SmartContractCode::Type get_code_type() {
return Traits::code_type;
}
static td::optional<td::int32> guess_revision(const vm::Cell::Hash& code_hash) {
for (auto i : ton::SmartContractCode::get_revisions(get_code_type())) {
auto code = SmartContractCode::get_code(get_code_type(), i);
if (code->get_hash() == code_hash) {
return i;
}
}
return {};
}
static td::Ref<WalletT> create(const InitData& init_data, int revision) {
return td::Ref<WalletT>(true, State{get_init_code(revision), WalletT::get_init_data(init_data)});
}
td::Result<td::uint32> get_seqno() const {
return TRY_VM([&]() -> td::Result<td::uint32> {
Answer answer = this->run_get_method("seqno");
if (!answer.success) {
return td::Status::Error("seqno get method failed");
}
return static_cast<td::uint32>(answer.stack.write().pop_long_range(std::numeric_limits<td::uint32>::max()));
}());
}
td::Result<td::uint32> get_wallet_id() const {
return TRY_VM([&]() -> td::Result<td::uint32> {
Answer answer = this->run_get_method("wallet_id");
if (!answer.success) {
return td::Status::Error("seqno get method failed");
}
return static_cast<td::uint32>(answer.stack.write().pop_long_range(std::numeric_limits<td::uint32>::max()));
}());
}
td::Result<td::uint64> get_balance(td::uint64 account_balance, td::uint32 now) const {
return TRY_VM([&]() -> td::Result<td::uint64> {
Answer answer = this->run_get_method(Args().set_method_id("balance").set_balance(account_balance).set_now(now));
if (!answer.success) {
return td::Status::Error("balance get method failed");
}
return static_cast<td::uint64>(answer.stack.write().pop_long());
}());
}
td::Result<td::Ed25519::PublicKey> get_public_key() const override {
return TRY_VM([&]() -> td::Result<td::Ed25519::PublicKey> {
Answer answer = this->run_get_method("get_public_key");
if (!answer.success) {
return td::Status::Error("get_public_key get method failed");
}
auto key_int = answer.stack.write().pop_int();
LOG(ERROR) << key_int->bit_size(false);
td::SecureString bytes(32);
if (!key_int->export_bytes(bytes.as_mutable_slice().ubegin(), bytes.size(), false)) {
return td::Status::Error("not a public key");
}
return td::Ed25519::PublicKey(std::move(bytes));
}());
};
};
struct RestrictedWalletTraits {
struct InitData {
td::SecureString init_key;
td::SecureString main_key;
td::uint32 wallet_id{0};
};
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
static constexpr unsigned max_gifts_size = 4;
static constexpr auto code_type = SmartContractCode::RestrictedWallet;
};
class RestrictedWallet : public WalletBase<RestrictedWallet, RestrictedWalletTraits> {
public:
struct Config {
td::uint32 start_at{0};
std::vector<std::pair<td::int32, td::uint64>> limits;
};
explicit RestrictedWallet(State state) : WalletBase(std::move(state)) {
}
td::Result<Config> get_config() const {
return TRY_VM([this]() -> td::Result<Config> {
auto cs = vm::load_cell_slice(get_state().data);
Config config;
td::Ref<vm::Cell> dict_root;
auto ok = cs.advance(32 + 32 + 256) && cs.fetch_uint_to(32, config.start_at) && cs.fetch_maybe_ref(dict_root);
vm::Dictionary dict(std::move(dict_root), 32);
dict.check_for_each([&](auto cs, auto ptr, auto ptr_bits) {
auto r_seconds = td::narrow_cast_safe<td::int32>(dict.key_as_integer(ptr, true)->to_long());
if (r_seconds.is_error()) {
ok = false;
return ok;
}
td::uint64 value;
ok &= smc::unpack_grams(cs, value);
config.limits.emplace_back(r_seconds.ok(), value);
return ok;
});
if (!ok) {
return td::Status::Error("Can't parse config");
}
std::sort(config.limits.begin(), config.limits.end());
return config;
}());
}
static td::Ref<vm::Cell> get_init_data(const InitData& init_data) {
vm::CellBuilder cb;
cb.store_long(0, 32);
cb.store_long(init_data.wallet_id, 32);
CHECK(init_data.init_key.size() == 32);
CHECK(init_data.main_key.size() == 32);
cb.store_bytes(init_data.init_key.as_slice());
cb.store_bytes(init_data.main_key.as_slice());
return cb.finalize();
}
td::Result<td::Ref<vm::Cell>> get_init_message(const td::Ed25519::PrivateKey& init_private_key,
td::uint32 valid_until, const Config& config) const {
vm::CellBuilder cb;
TRY_RESULT(seqno, get_seqno());
TRY_RESULT(wallet_id, get_wallet_id());
LOG(ERROR) << "seqno: " << seqno << " wallet_id: " << wallet_id;
if (seqno != 0) {
return td::Status::Error("Wallet is already inited");
}
cb.store_long(wallet_id, 32);
cb.store_long(valid_until, 32);
cb.store_long(seqno, 32);
cb.store_long(config.start_at, 32);
vm::Dictionary dict(32);
auto add = [&](td::int32 till, td::uint64 value) {
auto key = dict.integer_key(td::make_refint(till), 32, true);
vm::CellBuilder gcb;
block::tlb::t_Grams.store_integer_value(gcb, td::BigInt256(value));
dict.set_builder(key.bits(), 32, gcb);
};
for (auto limit : config.limits) {
add(limit.first, limit.second);
}
cb.store_maybe_ref(dict.get_root_cell());
auto message_outer = cb.finalize();
auto signature = init_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::Result<td::Ref<vm::Cell>> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 valid_until,
td::Span<Gift> gifts) const override {
CHECK(gifts.size() <= Traits::max_gifts_size);
vm::CellBuilder cb;
TRY_RESULT(seqno, get_seqno());
TRY_RESULT(wallet_id, get_wallet_id());
if (seqno == 0) {
return td::Status::Error("Wallet is not inited yet");
}
cb.store_long(wallet_id, 32);
cb.store_long(valid_until, 32);
cb.store_long(seqno, 32);
for (auto& gift : gifts) {
td::int32 send_mode = 3;
if (gift.gramms == -1) {
send_mode += 128;
}
cb.store_long(send_mode, 8).store_ref(create_int_message(gift));
}
auto message_outer = cb.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();
}
};
} // namespace ton

View file

@ -1542,6 +1542,7 @@ template <class DeserializerT>
class BenchBocDeserializer : public td::Benchmark { class BenchBocDeserializer : public td::Benchmark {
public: public:
BenchBocDeserializer(std::string name, BenchBocDeserializerConfig config) : name_(std::move(name)), config_(config) { BenchBocDeserializer(std::string name, BenchBocDeserializerConfig config) : name_(std::move(name)), config_(config) {
td::PerfWarningTimer perf("A", 1);
fast_array_ = vm::FastCompactArray(array_size); fast_array_ = vm::FastCompactArray(array_size);
td::Random::Xorshift128plus rnd{123}; td::Random::Xorshift128plus rnd{123};
for (td::uint32 i = 0; i < array_size; i++) { for (td::uint32 i = 0; i < array_size; i++) {

View file

@ -21,7 +21,9 @@
#include "Ed25519.h" #include "Ed25519.h"
#include "block/block-auto.h"
#include "block/block.h" #include "block/block.h"
#include "block/block-parse.h"
#include "fift/Fift.h" #include "fift/Fift.h"
#include "fift/words.h" #include "fift/words.h"
@ -38,6 +40,7 @@
#include "smc-envelope/WalletV3.h" #include "smc-envelope/WalletV3.h"
#include "smc-envelope/HighloadWallet.h" #include "smc-envelope/HighloadWallet.h"
#include "smc-envelope/HighloadWalletV2.h" #include "smc-envelope/HighloadWalletV2.h"
#include "smc-envelope/PaymentChannel.h"
#include "td/utils/base64.h" #include "td/utils/base64.h"
#include "td/utils/crypto.h" #include "td/utils/crypto.h"
@ -489,6 +492,99 @@ TEST(Tonlib, TestGiver) {
CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == res->get_hash()); CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == res->get_hash());
} }
TEST(Tonlib, RestrictedWallet) {
//auto source_lookup = fift::create_mem_source_lookup(load_source("smartcont/new-restricted-wallet2.fif")).move_as_ok();
//source_lookup
//.write_file("/auto/restricted-wallet2-code.fif", load_source("smartcont/auto/restricted-wallet2-code.fif"))
//.ensure();
//class ZeroOsTime : public fift::OsTime {
//public:
//td::uint32 now() override {
//return 0;
//}
//};
//source_lookup.set_os_time(std::make_unique<ZeroOsTime>());
//auto priv_key = td::Ed25519::generate_private_key().move_as_ok();
//auto pub_key = priv_key.get_public_key().move_as_ok();
//auto pub_key_serialized = block::PublicKey::from_bytes(pub_key.as_octet_string()).move_as_ok().serialize(true);
//std::vector<std::string> args = {"path", pub_key_serialized, std::string("100")};
//auto fift_output = fift::mem_run_fift(std::move(source_lookup), args).move_as_ok();
//ton::RestrictedWallet::InitData init_data;
//td::uint64 x = 100 * 1000000000ull;
//init_data.key = &pub_key;
//init_data.start_at = 0;
//init_data.limits = {{-32768, x}, {92, x * 3 / 4}, {183, x * 1 / 2}, {366, x * 1 / 4}, {548, 0}};
//auto wallet = ton::RestrictedWallet::create(init_data, -1);
//ASSERT_EQ(0u, wallet->get_seqno().move_as_ok());
//CHECK(pub_key.as_octet_string() == wallet->get_public_key().move_as_ok().as_octet_string());
////LOG(ERROR) << wallet->get_balance(x, 60 * 60 * 24 * 400).move_as_ok();
//auto new_wallet_query = fift_output.source_lookup.read_file("rwallet-query.boc").move_as_ok().data;
//auto new_wallet_addr = fift_output.source_lookup.read_file("rwallet.addr").move_as_ok().data;
//auto address = wallet->get_address(-1);
////CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32));
//address.bounceable = false;
//auto res = ton::GenericAccount::create_ext_message(address, wallet->get_init_state(),
//wallet->get_init_message(priv_key).move_as_ok());
//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());
//auto dest = block::StdAddress::parse("Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX").move_as_ok();
//fift_output.source_lookup.write_file("/main.fif", load_source("smartcont/wallet-v2.fif")).ensure();
//fift_output.source_lookup.write_file("rwallet.pk", priv_key.as_octet_string().as_slice()).ensure();
//fift_output = fift::mem_run_fift(
//std::move(fift_output.source_lookup),
//{"aba", "rwallet", "-C", "TESTv2", "Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "0", "321"})
//.move_as_ok();
//auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data;
//ton::TestWallet::Gift gift;
//gift.destination = dest;
//gift.message = "TESTv2";
//gift.gramms = 321000000000ll;
////CHECK(priv_key.get_public_key().ok().as_octet_string() == wallet->get_public_key().ok().as_octet_string());
//auto gift_message = ton::GenericAccount::create_ext_message(
//address, {}, wallet->make_a_gift_message(priv_key, 60, {gift}).move_as_ok());
//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, RestrictedWallet3) {
auto init_priv_key = td::Ed25519::generate_private_key().move_as_ok();
auto init_pub_key = init_priv_key.get_public_key().move_as_ok();
auto priv_key = td::Ed25519::generate_private_key().move_as_ok();
auto pub_key = priv_key.get_public_key().move_as_ok();
ton::RestrictedWallet::InitData init_data;
init_data.init_key = init_pub_key.as_octet_string();
init_data.main_key = pub_key.as_octet_string();
init_data.wallet_id = 123;
auto wallet = ton::RestrictedWallet::create(init_data, 1);
auto address = wallet->get_address();
td::uint64 x = 100 * 1000000000ull;
ton::RestrictedWallet::Config config;
config.start_at = 1;
config.limits = {{-32768, x}, {92, x * 3 / 4}, {183, x * 1 / 2}, {366, x * 1 / 4}, {548, 0}};
CHECK(wallet.write().send_external_message(wallet->get_init_message(init_priv_key, 10, config).move_as_ok()).success);
CHECK(wallet->get_seqno().move_as_ok() == 1);
ton::WalletInterface::Gift gift;
gift.destination = address;
gift.message = "hello";
CHECK(wallet.write().send_external_message(wallet->make_a_gift_message(priv_key, 10, {gift}).move_as_ok()).success);
CHECK(wallet->get_seqno().move_as_ok() == 2);
}
class SimpleWallet : public ton::SmartContract { class SimpleWallet : public ton::SmartContract {
public: public:
SimpleWallet(State state) : SmartContract(std::move(state)) { SimpleWallet(State state) : SmartContract(std::move(state)) {
@ -1286,3 +1382,397 @@ TEST(Smartcont, DnsManual) {
// TODO: rethink semantic of creating an empty dictionary // TODO: rethink semantic of creating an empty dictionary
do_dns_test(CheckedDns(true, true)); do_dns_test(CheckedDns(true, true));
} }
using namespace ton::pchan;
template <class T>
struct ValidateState {
T& self() {
return static_cast<T&>(*this);
}
void init(td::Ref<vm::Cell> state) {
state_ = state;
block::gen::ChanData::Record data_rec;
if (!tlb::unpack_cell(state, data_rec)) {
on_fatal_error(td::Status::Error("Expected Data"));
return;
}
if (!tlb::unpack_cell(data_rec.state, self().rec)) {
on_fatal_error(td::Status::Error("Expected StatePayout"));
return;
}
CHECK(self().rec.A.not_null());
}
T& expect_grams(td::Ref<vm::CellSlice> cs, td::uint64 expected, td::Slice name) {
if (has_fatal_error_) {
return self();
}
td::RefInt256 got;
CHECK(cs.not_null());
CHECK(block::tlb::t_Grams.as_integer_to(cs, got));
if (got->cmp(expected) != 0) {
on_error(td::Status::Error(PSLICE() << name << ": expected " << expected << ", got " << got->to_dec_string()));
}
return self();
}
template <class S>
T& expect_eq(S a, S expected, td::Slice name) {
if (has_fatal_error_) {
return self();
}
if (!(a == expected)) {
on_error(td::Status::Error(PSLICE() << name << ": expected " << expected << ", got " << a));
}
return self();
}
td::Status finish() {
if (errors_.empty()) {
return td::Status::OK();
}
std::stringstream ss;
block::gen::t_ChanData.print_ref(ss, state_);
td::StringBuilder sb;
for (auto& error : errors_) {
sb << error << "\n";
}
sb << ss.str();
return td::Status::Error(sb.as_cslice());
}
void on_fatal_error(td::Status error) {
CHECK(!has_fatal_error_);
has_fatal_error_ = true;
on_error(std::move(error));
}
void on_error(td::Status error) {
CHECK(error.is_error());
errors_.push_back(std::move(error));
}
public:
td::Ref<vm::Cell> state_;
bool has_fatal_error_{false};
std::vector<td::Status> errors_;
};
struct ValidateStatePayout : public ValidateState<ValidateStatePayout> {
ValidateStatePayout& expect_A(td::uint64 a) {
expect_grams(rec.A, a, "A");
return *this;
}
ValidateStatePayout& expect_B(td::uint64 b) {
expect_grams(rec.B, b, "B");
return *this;
}
ValidateStatePayout(td::Ref<vm::Cell> state) {
init(std::move(state));
}
block::gen::ChanState::Record_chan_state_payout rec;
};
struct ValidateStateInit : public ValidateState<ValidateStateInit> {
ValidateStateInit& expect_A(td::uint64 a) {
expect_grams(rec.A, a, "A");
return *this;
}
ValidateStateInit& expect_B(td::uint64 b) {
expect_grams(rec.B, b, "B");
return *this;
}
ValidateStateInit& expect_min_A(td::uint64 a) {
expect_grams(rec.min_A, a, "min_A");
return *this;
}
ValidateStateInit& expect_min_B(td::uint64 b) {
expect_grams(rec.min_B, b, "min_B");
return *this;
}
ValidateStateInit& expect_expire_at(td::uint32 b) {
expect_eq(rec.expire_at, b, "expire_at");
return *this;
}
ValidateStateInit& expect_signed_A(bool x) {
expect_eq(rec.signed_A, x, "signed_A");
return *this;
}
ValidateStateInit& expect_signed_B(bool x) {
expect_eq(rec.signed_B, x, "signed_B");
return *this;
}
ValidateStateInit(td::Ref<vm::Cell> state) {
init(std::move(state));
}
block::gen::ChanState::Record_chan_state_init rec;
};
struct ValidateStateClose : public ValidateState<ValidateStateClose> {
ValidateStateClose& expect_A(td::uint64 a) {
expect_grams(rec.A, a, "A");
return *this;
}
ValidateStateClose& expect_B(td::uint64 b) {
expect_grams(rec.B, b, "B");
return *this;
}
ValidateStateClose& expect_promise_A(td::uint64 a) {
expect_grams(rec.promise_A, a, "promise_A");
return *this;
}
ValidateStateClose& expect_promise_B(td::uint64 b) {
expect_grams(rec.promise_B, b, "promise_B");
return *this;
}
ValidateStateClose& expect_expire_at(td::uint32 b) {
expect_eq(rec.expire_at, b, "expire_at");
return *this;
}
ValidateStateClose& expect_signed_A(bool x) {
expect_eq(rec.signed_A, x, "signed_A");
return *this;
}
ValidateStateClose& expect_signed_B(bool x) {
expect_eq(rec.signed_B, x, "signed_B");
return *this;
}
ValidateStateClose(td::Ref<vm::Cell> state) {
init(std::move(state));
}
block::gen::ChanState::Record_chan_state_close rec;
};
// config$_ initTimeout:int exitTimeout:int a_key:int256 b_key:int256 a_addr b_addr channel_id:int256 = Config;
TEST(Smarcont, Channel) {
auto code = ton::SmartContractCode::get_code(ton::SmartContractCode::PaymentChannel);
Config config;
auto a_pkey = td::Ed25519::generate_private_key().move_as_ok();
auto b_pkey = td::Ed25519::generate_private_key().move_as_ok();
config.init_timeout = 20;
config.close_timeout = 40;
auto dest = block::StdAddress::parse("Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX").move_as_ok();
config.a_addr = dest;
config.b_addr = dest;
config.a_key = a_pkey.get_public_key().ok().as_octet_string();
config.b_key = b_pkey.get_public_key().ok().as_octet_string();
config.channel_id = 123;
Data data;
data.config = config.serialize();
data.state = data.init_state();
auto data_cell = data.serialize();
auto channel = ton::SmartContract::create(ton::SmartContract::State{code, data_cell});
ValidateStateInit(channel->get_state().data)
.expect_A(0)
.expect_B(0)
.expect_min_A(0)
.expect_min_B(0)
.expect_signed_A(false)
.expect_signed_B(false)
.expect_expire_at(0)
.finish()
.ensure();
enum err {
ok = 0,
wrong_a_signature = 31,
wrong_b_signature,
msg_value_too_small,
replay_protection,
no_timeout,
expected_init,
expected_close,
no_promise_signature,
wrong_channel_id
};
#define expect_code(description, expected_code, e) \
{ \
auto res = e; \
LOG_IF(FATAL, expected_code != res.code) << " res.code=" << res.code << " " << description << "\n" << #e; \
}
#define expect_ok(description, e) expect_code(description, 0, e)
expect_code("Trying to invoke a timeout while channel is empty", no_timeout,
channel.write().send_external_message(MsgTimeoutBuilder().finalize(),
ton::SmartContract::Args().set_now(1000000)));
expect_code("External init message with no signatures", replay_protection,
channel.write().send_external_message(MsgInitBuilder().channel_id(config.channel_id).finalize()));
expect_code("Internal init message with not enough value", msg_value_too_small,
channel.write().send_internal_message(
MsgInitBuilder().channel_id(config.channel_id).inc_A(1000).min_B(2000).with_a_key(&a_pkey).finalize(),
ton::SmartContract::Args().set_amount(100)));
expect_code(
"Internal init message with wrong channel_id", wrong_channel_id,
channel.write().send_internal_message(MsgInitBuilder().inc_A(1000).min_B(2000).with_a_key(&a_pkey).finalize(),
ton::SmartContract::Args().set_amount(1000)));
expect_ok("A init with (inc_A = 1000, min_A = 1, min_B = 2000)",
channel.write().send_internal_message(MsgInitBuilder()
.channel_id(config.channel_id)
.inc_A(1000)
.min_A(1)
.min_B(2000)
.with_a_key(&a_pkey)
.finalize(),
ton::SmartContract::Args().set_amount(1000)));
ValidateStateInit(channel->get_state().data)
.expect_A(1000)
.expect_B(0)
.expect_min_A(1)
.expect_min_B(2000)
.expect_signed_A(true)
.expect_signed_B(false)
.expect_expire_at(config.init_timeout)
.finish()
.ensure();
expect_code("Repeated init of A init with (inc_A = 100, min_B = 5000). Must be ignored", replay_protection,
channel.write().send_internal_message(
MsgInitBuilder().channel_id(config.channel_id).inc_A(100).min_B(5000).with_a_key(&a_pkey).finalize(),
ton::SmartContract::Args().set_amount(1000)));
expect_code(
"Trying to invoke a timeout too early", no_timeout,
channel.write().send_external_message(MsgTimeoutBuilder().finalize(), ton::SmartContract::Args().set_now(0)));
{
auto channel_copy = channel;
expect_ok("Invoke a timeout", channel_copy.write().send_external_message(MsgTimeoutBuilder().finalize(),
ton::SmartContract::Args().set_now(21)));
ValidateStatePayout(channel_copy->get_state().data).expect_A(1000).expect_B(0).finish().ensure();
}
{
auto channel_copy = channel;
expect_ok("B init with inc_B < min_B. Leads to immediate payout",
channel_copy.write().send_internal_message(
MsgInitBuilder().channel_id(config.channel_id).inc_B(1500).with_b_key(&b_pkey).finalize(),
ton::SmartContract::Args().set_amount(1500)));
ValidateStatePayout(channel_copy->get_state().data).expect_A(1000).expect_B(1500).finish().ensure();
}
expect_ok("B init with (inc_B = 2000, min_A = 1, min_A = 1000)",
channel.write().send_internal_message(
MsgInitBuilder().channel_id(config.channel_id).inc_B(2000).min_A(1000).with_b_key(&b_pkey).finalize(),
ton::SmartContract::Args().set_amount(2000)));
ValidateStateClose(channel->get_state().data)
.expect_A(1000)
.expect_B(2000)
.expect_promise_A(0)
.expect_promise_B(0)
.expect_signed_A(false)
.expect_signed_B(false)
.expect_expire_at(0)
.finish()
.ensure();
{
auto channel_copy = channel;
expect_ok("A&B send Promise(1000000, 1000000 + 10) signed by nobody",
channel_copy.write().send_external_message(MsgCloseBuilder()
.signed_promise(SignedPromiseBuilder()
.promise_A(1000000)
.promise_B(1000000 + 10)
.channel_id(config.channel_id)
.finalize())
.with_a_key(&a_pkey)
.with_b_key(&b_pkey)
.finalize(),
ton::SmartContract::Args().set_now(21)));
ValidateStatePayout(channel_copy->get_state().data).expect_A(1000 + 10).expect_B(2000 - 10).finish().ensure();
}
{
auto channel_copy = channel;
expect_ok("A&B send Promise(1000000, 1000000 + 10) signed by A",
channel_copy.write().send_external_message(MsgCloseBuilder()
.signed_promise(SignedPromiseBuilder()
.promise_A(1000000)
.promise_B(1000000 + 10)
.with_key(&a_pkey)
.channel_id(config.channel_id)
.finalize())
.with_a_key(&a_pkey)
.with_b_key(&b_pkey)
.finalize(),
ton::SmartContract::Args().set_now(21)));
ValidateStatePayout(channel_copy->get_state().data).expect_A(1000 + 10).expect_B(2000 - 10).finish().ensure();
}
expect_code(
"A sends Promise(1000000, 0) signed by A", wrong_b_signature,
channel.write().send_external_message(
MsgCloseBuilder()
.signed_promise(
SignedPromiseBuilder().promise_A(1000000).with_key(&a_pkey).channel_id(config.channel_id).finalize())
.with_a_key(&a_pkey)
.finalize(),
ton::SmartContract::Args().set_now(21)));
expect_code(
"B sends Promise(1000000, 0) signed by B", wrong_a_signature,
channel.write().send_external_message(
MsgCloseBuilder()
.signed_promise(
SignedPromiseBuilder().promise_A(1000000).with_key(&b_pkey).channel_id(config.channel_id).finalize())
.with_b_key(&b_pkey)
.finalize(),
ton::SmartContract::Args().set_now(21)));
expect_code("B sends Promise(1000000, 0) signed by A with wrong channel_id", wrong_channel_id,
channel.write().send_external_message(MsgCloseBuilder()
.signed_promise(SignedPromiseBuilder()
.promise_A(1000000)
.with_key(&a_pkey)
.channel_id(config.channel_id + 1)
.finalize())
.with_b_key(&b_pkey)
.finalize(),
ton::SmartContract::Args().set_now(21)));
expect_code(
"B sends unsigned Promise(1000000, 0)", no_promise_signature,
channel.write().send_external_message(
MsgCloseBuilder()
.signed_promise(SignedPromiseBuilder().promise_A(1000000).channel_id(config.channel_id).finalize())
.with_b_key(&b_pkey)
.finalize(),
ton::SmartContract::Args().set_now(21)));
expect_ok(
"B sends Promise(1000000, 0) signed by A",
channel.write().send_external_message(
MsgCloseBuilder()
.signed_promise(
SignedPromiseBuilder().promise_A(1000000).with_key(&a_pkey).channel_id(config.channel_id).finalize())
.with_b_key(&b_pkey)
.finalize(),
ton::SmartContract::Args().set_now(21)));
ValidateStateClose(channel->get_state().data)
.expect_A(1000)
.expect_B(2000)
.expect_promise_A(1000000)
.expect_promise_B(0)
.expect_signed_A(false)
.expect_signed_B(true)
.expect_expire_at(21 + config.close_timeout)
.finish()
.ensure();
expect_ok("B sends Promise(0, 1000000 + 10) signed by A",
channel.write().send_external_message(MsgCloseBuilder()
.signed_promise(SignedPromiseBuilder()
.promise_B(1000000 + 10)
.with_key(&b_pkey)
.channel_id(config.channel_id)
.finalize())
.with_a_key(&a_pkey)
.finalize(),
ton::SmartContract::Args().set_now(21)));
ValidateStatePayout(channel->get_state().data).expect_A(1000 + 10).expect_B(2000 - 10).finish().ensure();
#undef expect_ok
#undef expect_code
}

View file

@ -19,6 +19,7 @@
#include "vm/cells/CellSlice.h" #include "vm/cells/CellSlice.h"
#include "vm/excno.hpp" #include "vm/excno.hpp"
#include "td/utils/bits.h" #include "td/utils/bits.h"
#include "td/utils/misc.h"
namespace vm { namespace vm {
@ -719,6 +720,10 @@ bool CellSlice::prefetch_bytes(unsigned char* buffer, unsigned bytes) const {
} }
} }
bool CellSlice::fetch_bytes(td::MutableSlice slice) {
return fetch_bytes(slice.ubegin(), td::narrow_cast<unsigned>(slice.size()));
}
bool CellSlice::fetch_bytes(unsigned char* buffer, unsigned bytes) { bool CellSlice::fetch_bytes(unsigned char* buffer, unsigned bytes) {
if (prefetch_bytes(buffer, bytes)) { if (prefetch_bytes(buffer, bytes)) {
advance(bytes * 8); advance(bytes * 8);
@ -728,6 +733,10 @@ bool CellSlice::fetch_bytes(unsigned char* buffer, unsigned bytes) {
} }
} }
bool CellSlice::prefetch_bytes(td::MutableSlice slice) const {
return prefetch_bytes(slice.ubegin(), td::narrow_cast<unsigned>(slice.size()));
}
Ref<Cell> CellSlice::prefetch_ref(unsigned offset) const { Ref<Cell> CellSlice::prefetch_ref(unsigned offset) const {
if (offset < size_refs()) { if (offset < size_refs()) {
auto ref_id = refs_st + offset; auto ref_id = refs_st + offset;

View file

@ -218,7 +218,9 @@ class CellSlice : public td::CntObject {
return prefetch_bits_to(buffer.bits(), n); return prefetch_bits_to(buffer.bits(), n);
} }
bool fetch_bytes(unsigned char* buffer, unsigned bytes); bool fetch_bytes(unsigned char* buffer, unsigned bytes);
bool fetch_bytes(td::MutableSlice slice);
bool prefetch_bytes(unsigned char* buffer, unsigned bytes) const; bool prefetch_bytes(unsigned char* buffer, unsigned bytes) const;
bool prefetch_bytes(td::MutableSlice slice) const;
td::BitSlice as_bitslice() const { td::BitSlice as_bitslice() const {
return prefetch_bits(size()); return prefetch_bits(size());
} }

View file

@ -148,6 +148,11 @@ class MerkleProofCombineFast {
MerkleProofCombineFast(Ref<Cell> a, Ref<Cell> b) : a_(std::move(a)), b_(std::move(b)) { MerkleProofCombineFast(Ref<Cell> a, Ref<Cell> b) : a_(std::move(a)), b_(std::move(b)) {
} }
td::Result<Ref<Cell>> run() { td::Result<Ref<Cell>> run() {
if (a_.is_null()) {
return b_;
} else if (b_.is_null()) {
return a_;
}
TRY_RESULT_ASSIGN(a_, unpack_proof(a_)); TRY_RESULT_ASSIGN(a_, unpack_proof(a_));
TRY_RESULT_ASSIGN(b_, unpack_proof(b_)); TRY_RESULT_ASSIGN(b_, unpack_proof(b_));
TRY_RESULT(res, run_raw()); TRY_RESULT(res, run_raw());
@ -204,6 +209,11 @@ class MerkleProofCombine {
MerkleProofCombine(Ref<Cell> a, Ref<Cell> b) : a_(std::move(a)), b_(std::move(b)) { MerkleProofCombine(Ref<Cell> a, Ref<Cell> b) : a_(std::move(a)), b_(std::move(b)) {
} }
td::Result<Ref<Cell>> run() { td::Result<Ref<Cell>> run() {
if (a_.is_null()) {
return b_;
} else if (b_.is_null()) {
return a_;
}
TRY_RESULT_ASSIGN(a_, unpack_proof(a_)); TRY_RESULT_ASSIGN(a_, unpack_proof(a_));
TRY_RESULT_ASSIGN(b_, unpack_proof(b_)); TRY_RESULT_ASSIGN(b_, unpack_proof(b_));
TRY_RESULT(res, run_raw()); TRY_RESULT(res, run_raw());
@ -323,6 +333,10 @@ Ref<Cell> MerkleProof::combine(Ref<Cell> a, Ref<Cell> b) {
return res.move_as_ok(); return res.move_as_ok();
} }
td::Result<Ref<Cell>> MerkleProof::combine_status(Ref<Cell> a, Ref<Cell> b) {
return MerkleProofCombine(std::move(a), std::move(b)).run();
}
Ref<Cell> MerkleProof::combine_fast(Ref<Cell> a, Ref<Cell> b) { Ref<Cell> MerkleProof::combine_fast(Ref<Cell> a, Ref<Cell> b) {
auto res = MerkleProofCombineFast(std::move(a), std::move(b)).run(); auto res = MerkleProofCombineFast(std::move(a), std::move(b)).run();
if (res.is_error()) { if (res.is_error()) {
@ -331,6 +345,10 @@ Ref<Cell> MerkleProof::combine_fast(Ref<Cell> a, Ref<Cell> b) {
return res.move_as_ok(); return res.move_as_ok();
} }
td::Result<Ref<Cell>> MerkleProof::combine_fast_status(Ref<Cell> a, Ref<Cell> b) {
return MerkleProofCombineFast(std::move(a), std::move(b)).run();
}
Ref<Cell> MerkleProof::combine_raw(Ref<Cell> a, Ref<Cell> b) { Ref<Cell> MerkleProof::combine_raw(Ref<Cell> a, Ref<Cell> b) {
auto res = MerkleProofCombine(std::move(a), std::move(b)).run_raw(); auto res = MerkleProofCombine(std::move(a), std::move(b)).run_raw();
if (res.is_error()) { if (res.is_error()) {

View file

@ -38,7 +38,9 @@ class MerkleProof {
static Ref<Cell> virtualize(Ref<Cell> cell, int virtualization); static Ref<Cell> virtualize(Ref<Cell> cell, int virtualization);
static Ref<Cell> combine(Ref<Cell> a, Ref<Cell> b); static Ref<Cell> combine(Ref<Cell> a, Ref<Cell> b);
static td::Result<Ref<Cell>> combine_status(Ref<Cell> a, Ref<Cell> b);
static Ref<Cell> combine_fast(Ref<Cell> a, Ref<Cell> b); static Ref<Cell> combine_fast(Ref<Cell> a, Ref<Cell> b);
static td::Result<Ref<Cell>> combine_fast_status(Ref<Cell> a, Ref<Cell> b);
// works with upwrapped proofs // works with upwrapped proofs
// works fine with cell of non-zero level, but this is not supported (yet?) in MerkeProof special cell // works fine with cell of non-zero level, but this is not supported (yet?) in MerkeProof special cell

View file

@ -255,15 +255,15 @@ TonDbTransactionImpl::TonDbTransactionImpl(std::shared_ptr<KeyValue> kv) : kv_(s
} }
void TonDbTransactionImpl::begin() { void TonDbTransactionImpl::begin() {
kv_->begin_transaction(); kv_->begin_write_batch();
generation_++; generation_++;
} }
void TonDbTransactionImpl::commit() { void TonDbTransactionImpl::commit() {
kv_->commit_transaction(); kv_->commit_write_batch();
reader_.reset(kv_->snapshot().release()); reader_.reset(kv_->snapshot().release());
} }
void TonDbTransactionImpl::abort() { void TonDbTransactionImpl::abort() {
kv_->abort_transaction(); kv_->abort_write_batch();
} }
void TonDbTransactionImpl::clear_cache() { void TonDbTransactionImpl::clear_cache() {
contracts_ = {}; contracts_ = {};

View file

@ -77,6 +77,13 @@ class VmError {
long long get_arg() const { long long get_arg() const {
return arg; return arg;
} }
td::Status as_status() const {
return td::Status::Error(td::Slice{get_msg()});
}
template <typename T>
td::Status as_status(T pfx) const {
return td::Status::Error(PSLICE() << pfx << get_msg());
}
}; };
struct VmNoGas { struct VmNoGas {
@ -90,6 +97,13 @@ struct VmNoGas {
operator VmError() const { operator VmError() const {
return VmError{Excno::out_of_gas, "out of gas"}; return VmError{Excno::out_of_gas, "out of gas"};
} }
td::Status as_status() const {
return td::Status::Error(td::Slice{get_msg()});
}
template <typename T>
td::Status as_status(T pfx) const {
return td::Status::Error(PSLICE() << pfx << get_msg());
}
}; };
struct VmVirtError { struct VmVirtError {
@ -106,6 +120,13 @@ struct VmVirtError {
operator VmError() const { operator VmError() const {
return VmError{Excno::virt_err, "prunned branch", virtualization}; return VmError{Excno::virt_err, "prunned branch", virtualization};
} }
td::Status as_status() const {
return td::Status::Error(td::Slice{get_msg()});
}
template <typename T>
td::Status as_status(T pfx) const {
return td::Status::Error(PSLICE() << pfx << get_msg());
}
}; };
struct VmFatal {}; struct VmFatal {};
@ -114,12 +135,12 @@ template <class F>
auto try_f(F&& f) noexcept -> decltype(f()) { auto try_f(F&& f) noexcept -> decltype(f()) {
try { try {
return f(); return f();
} catch (vm::VmError error) { } catch (vm::VmError& error) {
return td::Status::Error(PSLICE() << "Got a vm exception: " << error.get_msg()); return error.as_status("Got a vm exception: ");
} catch (vm::VmVirtError error) { } catch (vm::VmVirtError& error) {
return td::Status::Error(PSLICE() << "Got a vm virtualization exception: " << error.get_msg()); return error.as_status("Got a vm virtualization exception: ");
} catch (vm::VmNoGas error) { } catch (vm::VmNoGas& error) {
return td::Status::Error(PSLICE() << "Got a vm no gas exception: " << error.get_msg()); return error.as_status("Got a vm no gas exception: ");
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -33,6 +33,7 @@
#include "vm/cells.h" #include "vm/cells.h"
#include "vm/stack.hpp" #include "vm/stack.hpp"
#include "block/block.h" #include "block/block.h"
#include "block/mc-config.h"
#include "td/utils/filesystem.h" #include "td/utils/filesystem.h"
using td::Ref; using td::Ref;
@ -67,8 +68,8 @@ class TestNode : public td::actor::Actor {
ton::BlockIdExt last_block_id_, last_state_id_; ton::BlockIdExt last_block_id_, last_state_id_;
td::BufferSlice last_block_data_, last_state_data_; td::BufferSlice last_block_data_, last_state_data_;
ton::StdSmcAddress dns_root_; ton::StdSmcAddress dns_root_, elect_addr_;
bool dns_root_queried_{false}; bool dns_root_queried_{false}, elect_addr_queried_{false};
std::string line_; std::string line_;
const char *parse_ptr_, *parse_end_; const char *parse_ptr_, *parse_end_;
@ -89,6 +90,9 @@ class TestNode : public td::actor::Actor {
std::unique_ptr<ton::adnl::AdnlExtClient::Callback> make_callback(); std::unique_ptr<ton::adnl::AdnlExtClient::Callback> make_callback();
using creator_stats_func_t =
std::function<bool(const td::Bits256&, const block::DiscountedCounter&, const block::DiscountedCounter&)>;
struct TransId { struct TransId {
ton::Bits256 acc_addr; ton::Bits256 acc_addr;
ton::LogicalTime trans_lt; ton::LogicalTime trans_lt;
@ -98,6 +102,75 @@ class TestNode : public td::actor::Actor {
} }
}; };
struct BlockHdrInfo {
ton::BlockIdExt blk_id;
Ref<vm::Cell> proof, virt_blk_root;
int mode;
BlockHdrInfo() : mode(-1) {
}
BlockHdrInfo(const ton::BlockIdExt blk_id_, Ref<vm::Cell> proof_, Ref<vm::Cell> vroot_, int mode_)
: blk_id(blk_id_), proof(std::move(proof_)), virt_blk_root(std::move(vroot_)), mode(mode_) {
}
};
struct ConfigInfo {
std::unique_ptr<block::Config> config;
Ref<vm::Cell> state_proof, config_proof;
ConfigInfo() = default;
ConfigInfo(std::unique_ptr<block::Config> config_, Ref<vm::Cell> state_proof_, Ref<vm::Cell> config_proof_)
: config(std::move(config_)), state_proof(std::move(state_proof_)), config_proof(std::move(config_proof_)) {
}
};
struct CreatorStatsRes {
int mode;
bool complete{false};
td::Bits256 last_key;
Ref<vm::Cell> state_proof, data_proof;
CreatorStatsRes(int mode_ = 0) : mode(mode_) {
last_key.set_zero();
}
CreatorStatsRes(int mode_, const td::Bits256& key_, Ref<vm::Cell> st_proof_ = {}, Ref<vm::Cell> dproof_ = {})
: mode(mode_), last_key(key_), state_proof(std::move(st_proof_)), data_proof(std::move(dproof_)) {
}
};
struct ValidatorLoadInfo {
ton::BlockIdExt blk_id;
Ref<vm::Cell> state_proof, data_proof, virt_root;
std::unique_ptr<block::Config> config;
ton::UnixTime block_created_at{0};
ton::UnixTime valid_since{0};
ton::LogicalTime end_lt{0};
ton::Bits256 vset_hash;
Ref<vm::Cell> vset_root;
std::unique_ptr<block::ValidatorSet> vset;
std::map<ton::Bits256, int> vset_map;
int special_idx{-1};
std::pair<td::int64, td::int64> created_total, created_special;
std::vector<std::pair<td::int64, td::int64>> created;
ValidatorLoadInfo(ton::BlockIdExt blkid, Ref<vm::Cell> root, Ref<vm::Cell> root2,
std::unique_ptr<block::Config> cfg = {})
: blk_id(blkid)
, state_proof(std::move(root))
, data_proof(std::move(root2))
, config(std::move(cfg))
, valid_since(0) {
}
td::Status unpack_vset();
bool store_record(const td::Bits256& key, const block::DiscountedCounter& mc_cnt,
const block::DiscountedCounter& shard_cnt);
bool has_data() const {
return blk_id.is_masterchain_ext() && state_proof.not_null() && data_proof.not_null() && config;
}
td::Status check_header_proof(ton::UnixTime* save_utime = nullptr, ton::LogicalTime* save_lt = nullptr) const;
td::Result<Ref<vm::Cell>> build_proof(int idx, td::Bits256* save_pubkey = nullptr) const;
td::Result<Ref<vm::Cell>> build_producer_info(int idx, td::Bits256* save_pubkey = nullptr) const;
td::Status init_check_proofs();
static td::Result<std::unique_ptr<ValidatorLoadInfo>> preinit_from_producer_info(Ref<vm::Cell> prod_info);
td::Status load_special_creator_stat(const td::Bits256& spec_pubkey, bool load_total = true);
};
void run_init_queries(); void run_init_queries();
char cur() const { char cur() const {
return *parse_ptr_; return *parse_ptr_;
@ -133,6 +206,8 @@ class TestNode : public td::actor::Actor {
std::vector<vm::StackEntry> params, td::BufferSlice remote_c7, td::BufferSlice remote_libs, std::vector<vm::StackEntry> params, td::BufferSlice remote_c7, td::BufferSlice remote_libs,
td::BufferSlice remote_result, int remote_exit_code, td::BufferSlice remote_result, int remote_exit_code,
td::Promise<std::vector<vm::StackEntry>> promise); td::Promise<std::vector<vm::StackEntry>> promise);
bool register_config_param(int idx, Ref<vm::Cell> value);
bool register_config_param1(Ref<vm::Cell> value);
bool register_config_param4(Ref<vm::Cell> value); bool register_config_param4(Ref<vm::Cell> value);
bool dns_resolve_start(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt blkid, std::string domain, bool dns_resolve_start(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt blkid, std::string domain,
int cat, int mode); int cat, int mode);
@ -144,18 +219,24 @@ class TestNode : public td::actor::Actor {
bool show_dns_record(std::ostream& os, int cat, Ref<vm::Cell> value, bool raw_dump); bool show_dns_record(std::ostream& os, int cat, Ref<vm::Cell> value, bool raw_dump);
bool get_all_shards(std::string filename = "", bool use_last = true, ton::BlockIdExt blkid = {}); bool get_all_shards(std::string filename = "", bool use_last = true, ton::BlockIdExt blkid = {});
void got_all_shards(ton::BlockIdExt blk, td::BufferSlice proof, td::BufferSlice data, std::string filename); void got_all_shards(ton::BlockIdExt blk, td::BufferSlice proof, td::BufferSlice data, std::string filename);
bool get_config_params(ton::BlockIdExt blkid, td::Promise<td::Unit> do_after, int mode = 0, std::string filename = "", bool parse_get_config_params(ton::BlockIdExt blkid, int mode = 0, std::string filename = "",
std::vector<int> params = {}); std::vector<int> params = {});
void got_config_params(ton::BlockIdExt req_blkid, ton::BlockIdExt blkid, td::BufferSlice state_proof, bool get_config_params(ton::BlockIdExt blkid, td::Promise<std::unique_ptr<block::Config>> promise, int mode = 0,
td::BufferSlice cfg_proof, int mode, std::string filename, std::vector<int> params, std::string filename = "", std::vector<int> params = {});
td::Promise<td::Unit> do_after); bool get_config_params_ext(ton::BlockIdExt blkid, td::Promise<ConfigInfo> promise, int mode = 0,
std::string filename = "", std::vector<int> params = {});
void got_config_params(ton::BlockIdExt req_blkid, int mode, std::string filename, std::vector<int> params,
td::Result<td::BufferSlice> R, td::Promise<ConfigInfo> promise);
bool get_block(ton::BlockIdExt blk, bool dump = false); bool get_block(ton::BlockIdExt blk, bool dump = false);
void got_block(ton::BlockIdExt blkid, td::BufferSlice data, bool dump); void got_block(ton::BlockIdExt blkid, td::BufferSlice data, bool dump);
bool get_state(ton::BlockIdExt blk, bool dump = false); bool get_state(ton::BlockIdExt blk, bool dump = false);
void got_state(ton::BlockIdExt blkid, ton::RootHash root_hash, ton::FileHash file_hash, td::BufferSlice data, void got_state(ton::BlockIdExt blkid, ton::RootHash root_hash, ton::FileHash file_hash, td::BufferSlice data,
bool dump); bool dump);
bool get_block_header(ton::BlockIdExt blk, int mode); bool get_show_block_header(ton::BlockIdExt blk, int mode);
bool lookup_block(ton::ShardIdFull shard, int mode, td::uint64 arg); bool get_block_header(ton::BlockIdExt blk, int mode, td::Promise<BlockHdrInfo> promise);
bool lookup_show_block(ton::ShardIdFull shard, int mode, td::uint64 arg);
bool lookup_block(ton::ShardIdFull shard, int mode, td::uint64 arg, td::Promise<BlockHdrInfo>);
void got_block_header_raw(td::BufferSlice res, td::Promise<BlockHdrInfo> promise, ton::BlockIdExt req_blkid = {});
void got_block_header(ton::BlockIdExt blkid, td::BufferSlice data, int mode); void got_block_header(ton::BlockIdExt blkid, td::BufferSlice data, int mode);
bool show_block_header(ton::BlockIdExt blkid, Ref<vm::Cell> root, int mode); bool show_block_header(ton::BlockIdExt blkid, Ref<vm::Cell> root, int mode);
bool show_state_header(ton::BlockIdExt blkid, Ref<vm::Cell> root, int mode); bool show_state_header(ton::BlockIdExt blkid, Ref<vm::Cell> root, int mode);
@ -177,9 +258,43 @@ class TestNode : public td::actor::Actor {
void got_block_proof(ton::BlockIdExt from, ton::BlockIdExt to, int mode, td::BufferSlice res); void got_block_proof(ton::BlockIdExt from, ton::BlockIdExt to, int mode, td::BufferSlice res);
bool get_creator_stats(ton::BlockIdExt blkid, int mode, unsigned req_count, ton::Bits256 start_after, bool get_creator_stats(ton::BlockIdExt blkid, int mode, unsigned req_count, ton::Bits256 start_after,
ton::UnixTime min_utime); ton::UnixTime min_utime);
void got_creator_stats(ton::BlockIdExt req_blkid, ton::BlockIdExt blkid, int req_mode, int mode, bool get_creator_stats(ton::BlockIdExt blkid, int mode, unsigned req_count, ton::Bits256 start_after,
td::Bits256 start_after, ton::UnixTime min_utime, td::BufferSlice state_proof, ton::UnixTime min_utime, creator_stats_func_t func, td::Promise<td::Bits256> promise);
td::BufferSlice data_proof, int count, int req_count, bool complete); bool get_creator_stats(ton::BlockIdExt blkid, unsigned req_count, ton::UnixTime min_utime, creator_stats_func_t func,
std::unique_ptr<CreatorStatsRes> state, td::Promise<std::unique_ptr<CreatorStatsRes>> promise);
void got_creator_stats(ton::BlockIdExt req_blkid, ton::BlockIdExt blkid, int mode, ton::UnixTime min_utime,
td::BufferSlice state_proof, td::BufferSlice data_proof, int count, int req_count,
bool complete, creator_stats_func_t func, std::unique_ptr<CreatorStatsRes> state,
td::Promise<std::unique_ptr<CreatorStatsRes>> promise);
bool check_validator_load(int start_time, int end_time, int mode = 0, std::string file_pfx = "");
void continue_check_validator_load(ton::BlockIdExt blkid1, Ref<vm::Cell> root1, ton::BlockIdExt blkid2,
Ref<vm::Cell> root2, int mode = 0, std::string file_pfx = "");
void continue_check_validator_load2(std::unique_ptr<ValidatorLoadInfo> info1,
std::unique_ptr<ValidatorLoadInfo> info2, int mode = 0,
std::string file_pfx = "");
void continue_check_validator_load3(std::unique_ptr<ValidatorLoadInfo> info1,
std::unique_ptr<ValidatorLoadInfo> info2, int mode = 0,
std::string file_pfx = "");
td::Status write_val_create_proof(ValidatorLoadInfo& info1, ValidatorLoadInfo& info2, int idx, bool severe,
std::string file_pfx, int cnt);
bool load_creator_stats(std::unique_ptr<ValidatorLoadInfo> load_to,
td::Promise<std::unique_ptr<ValidatorLoadInfo>> promise, bool need_proofs);
td::Status check_validator_load_proof(std::string filename, std::string vset_filename = "",
ton::Bits256 vset_hash = ton::Bits256::zero());
td::Status continue_check_validator_load_proof(std::unique_ptr<ValidatorLoadInfo> info1,
std::unique_ptr<ValidatorLoadInfo> info2, Ref<vm::Cell> root);
bool get_elector_addr(td::Promise<ton::StdSmcAddress> promise);
bool get_past_validator_sets();
bool send_past_vset_query(ton::StdSmcAddress elector_addr);
void register_past_vset_info(vm::StackEntry list);
bool get_complaints(unsigned elect_id, std::string file_pfx);
void send_get_complaints_query(unsigned elect_id, ton::StdSmcAddress elector_addr, std::string file_pfx);
void save_complaints(unsigned elect_id, Ref<vm::Cell> complaints, std::string file_pfx);
td::Status get_complaint_price(unsigned expires_in, std::string filename);
td::Status get_complaint_price(unsigned expires_in, unsigned bits, unsigned refs,
td::Bits256 chash = td::Bits256::zero(), std::string filename = "");
void send_compute_complaint_price_query(ton::StdSmcAddress elector_addr, unsigned expires_in, unsigned bits,
unsigned refs, td::Bits256 chash, std::string filename);
bool cache_cell(Ref<vm::Cell> cell); bool cache_cell(Ref<vm::Cell> cell);
bool list_cached_cells() const; bool list_cached_cells() const;
bool dump_cached_cell(td::Slice hash_pfx, td::Slice type_name = {}); bool dump_cached_cell(td::Slice hash_pfx, td::Slice type_name = {});
@ -222,6 +337,17 @@ class TestNode : public td::actor::Actor {
bool show_new_blkids(bool all = false); bool show_new_blkids(bool all = false);
bool complete_blkid(ton::BlockId partial_blkid, ton::BlockIdExt& complete_blkid) const; bool complete_blkid(ton::BlockId partial_blkid, ton::BlockIdExt& complete_blkid) const;
td::Promise<td::Unit> trivial_promise(); td::Promise<td::Unit> trivial_promise();
template <typename T>
td::Promise<T> trivial_promise_of() {
return td::PromiseCreator::lambda([Self = actor_id(this)](td::Result<T> res) {
if (res.is_error()) {
LOG(ERROR) << "error: " << res.move_as_error();
}
});
}
static ton::UnixTime now() {
return static_cast<td::uint32>(td::Clocks::system());
}
static const tlb::TypenameLookup& get_tlb_dict(); static const tlb::TypenameLookup& get_tlb_dict();
public: public:
@ -292,7 +418,8 @@ class TestNode : public td::actor::Actor {
//td::actor::SchedulerContext::get()->stop(); //td::actor::SchedulerContext::get()->stop();
} }
void got_result(); void got_result(td::Result<td::BufferSlice> R, td::Promise<td::BufferSlice> promise);
void after_got_result(bool ok);
bool envelope_send_query(td::BufferSlice query, td::Promise<td::BufferSlice> promise); bool envelope_send_query(td::BufferSlice query, td::Promise<td::BufferSlice> promise);
void parse_line(td::BufferSlice data); void parse_line(td::BufferSlice data);

View file

@ -44,8 +44,24 @@ class GetArg<R (C::*)(Arg) const> {
using type = Arg; using type = Arg;
}; };
template <typename T>
struct GetRet : public GetRet<decltype(&T::operator())> {};
template <class C, class R, class... Arg>
class GetRet<R (C::*)(Arg...)> {
public:
using type = R;
};
template <class C, class R, class... Arg>
class GetRet<R (C::*)(Arg...) const> {
public:
using type = R;
};
template <class T> template <class T>
using get_arg_t = std::decay_t<typename GetArg<T>::type>; using get_arg_t = std::decay_t<typename GetArg<T>::type>;
template <class T>
using get_ret_t = std::decay_t<typename GetRet<T>::type>;
template <class T> template <class T>
struct DropResult { struct DropResult {
@ -131,6 +147,7 @@ constexpr bool is_promise_interface_ptr() {
template <class ValueT, class FunctionT> template <class ValueT, class FunctionT>
class LambdaPromise : public PromiseInterface<ValueT> { class LambdaPromise : public PromiseInterface<ValueT> {
public: public:
using ArgT = ValueT;
void set_value(ValueT &&value) override { void set_value(ValueT &&value) override {
CHECK(has_lambda_.get()); CHECK(has_lambda_.get());
do_ok(std::move(value)); do_ok(std::move(value));
@ -288,12 +305,6 @@ class Promise {
std::unique_ptr<PromiseInterface<T>> promise_; std::unique_ptr<PromiseInterface<T>> promise_;
}; };
template <class F>
auto make_promise(F &&f) {
using ValueT = detail::drop_result_t<detail::get_arg_t<F>>;
return Promise<ValueT>(promise_interface_ptr(std::forward<F>(f)));
}
namespace detail { namespace detail {
template <class... ArgsT> template <class... ArgsT>
class JoinPromise : public PromiseInterface<Unit> { class JoinPromise : public PromiseInterface<Unit> {
@ -331,6 +342,16 @@ class PromiseCreator {
} }
}; };
template <class F>
auto make_promise(F &&f) {
using ValueT = typename decltype(PromiseCreator::lambda(std::move(f)))::ArgT;
return Promise<ValueT>(PromiseCreator::lambda(std::move(f)));
}
template <class T>
auto make_promise(Promise<T> &&f) {
return std::move(f);
}
template <class T = Unit> template <class T = Unit>
class SafePromise { class SafePromise {
public: public:
@ -356,4 +377,145 @@ class SafePromise {
Promise<T> promise_; Promise<T> promise_;
Result<T> result_; Result<T> result_;
}; };
template <class PromiseT, typename... ArgsT>
class PromiseMerger;
template <class F>
struct SplitPromise {
using PromiseT = decltype(make_promise(std::declval<F>()));
using ArgT = typename PromiseT::ArgT;
template <class S, class T>
static std::pair<Promise<S>, Promise<T>> split(std::pair<S, T>);
template <class... ArgsT>
static std::tuple<Promise<ArgsT>...> split(std::tuple<ArgsT...>);
using SplittedT = decltype(split(std::declval<ArgT>()));
template <class S, class T>
static PromiseMerger<PromiseT, S, T> merger(std::pair<S, T>);
template <class... ArgsT>
static PromiseMerger<PromiseT, ArgsT...> merger(std::tuple<ArgsT...>);
using MergerT = decltype(merger(std::declval<ArgT>()));
};
template <class PromiseT, typename... ArgsT>
class PromiseMerger : public std::enable_shared_from_this<PromiseMerger<PromiseT, ArgsT...>> {
public:
std::tuple<Result<ArgsT>...> args_;
PromiseT promise_;
PromiseMerger(PromiseT promise) : promise_(std::move(promise)) {
}
~PromiseMerger() {
td::Status status;
tuple_for_each(args_, [&status](auto &&arg) {
if (status.is_error()) {
return;
}
if (arg.is_error()) {
status = arg.move_as_error();
}
});
if (status.is_error()) {
promise_.set_error(std::move(status));
return;
}
call_tuple([this](auto &&... args) { promise_.set_value({args.move_as_ok()...}); }, std::move(args_));
}
template <class T>
Promise<typename T::ValueT> make_promise(T &arg) {
return [&arg, self = this->shared_from_this()](auto res) { arg = std::move(res); };
}
template <class R>
auto split() {
return call_tuple([this](auto &&... arg) { return R{this->make_promise(arg)...}; }, std::move(args_));
}
};
template <class F>
auto split_promise(F &&f) {
auto merger = std::make_shared<typename SplitPromise<F>::MergerT>(std::move(f));
return merger->template split<typename SplitPromise<F>::SplittedT>();
}
template <class T>
struct PromiseFuture {
Result<Promise<T>> promise_;
Result<T> result_;
~PromiseFuture() {
if (promise_.is_ok()) {
promise_.move_as_ok().set_result(std::move(result_));
} else {
LOG(ERROR) << "Lost PromiseFuture";
}
}
};
template <class T>
struct Future;
template <class T>
std::pair<Promise<T>, Future<T>> make_promise_future();
template <class T>
struct Future {
Promise<Promise<T>> promise_;
Future(Promise<Promise<T>> promise) : promise_(std::move(promise)) {
}
void finish(Promise<T> promise) {
promise_.set_value(std::move(promise));
}
template <class F>
auto map(F &&f) {
using R = detail::drop_result_t<decltype(f(std::declval<T>()))>;
auto pf = make_promise_future<R>();
promise_.set_value([p = std::move(pf.first), f = std::move(f)](Result<T> res) mutable {
TRY_RESULT_PROMISE(p, x, std::move(res));
p.set_result(f(std::move(x)));
});
return std::move(pf.second);
}
template <class F>
auto fmap(F &&f) {
return flatten(map(std::move(f)));
}
template <class X>
static Future<X> flatten(Future<Future<X>> ff) {
auto pf = make_promise_future<X>();
ff.promise_.set_value([p = std::move(pf.first)](Result<Future<X>> r_f) mutable {
TRY_RESULT_PROMISE(p, f, std::move(r_f));
// Promise<X> p
// Future<X> f
f.promise_.set_value(std::move(p));
});
return std::move(pf.second);
}
};
template <class T>
Future<T> make_future(T &&value) {
return Future<T>([value = std::move(value)](Result<Promise<T>> r_promise) mutable {
if (r_promise.is_ok()) {
r_promise.move_as_ok().set_value(std::move(value));
} else {
LOG(ERROR) << "Lost future";
}
});
}
template <class T>
std::pair<Promise<T>, Future<T>> make_promise_future() {
auto pf = std::make_shared<PromiseFuture<T>>();
Future<T> future([pf](Result<Promise<T>> res) mutable { pf->promise_ = std::move(res); });
Promise<T> promise = [pf = std::move(pf)](Result<T> res) mutable { pf->result_ = std::move(res); };
return std::make_pair(std::move(promise), std::move(future));
}
} // namespace td } // namespace td

View file

@ -100,6 +100,28 @@ void send_closure(ActorIdT &&actor_id, FunctionT function, ArgsT &&... args) {
#endif #endif
template <class ActorIdT, class FunctionT, class... ArgsT, class FunctionClassT = member_function_class_t<FunctionT>,
size_t argument_count = member_function_argument_count<FunctionT>(),
std::enable_if_t<argument_count == sizeof...(ArgsT), bool> with_promise = false>
auto future_send_closure(ActorIdT &&actor_id, FunctionT function, ArgsT &&... args) {
using R = ::td::detail::get_ret_t<std::decay_t<FunctionT>>;
auto pf = make_promise_future<R>();
send_closure(std::forward<ActorIdT>(actor_id), std::move(function), std::forward<ArgsT>(args)...,
std::move(pf.first));
return std::move(pf.second);
}
template <class R, class ActorIdT, class FunctionT, class... ArgsT,
class FunctionClassT = member_function_class_t<FunctionT>,
size_t argument_count = member_function_argument_count<FunctionT>(),
std::enable_if_t<argument_count != sizeof...(ArgsT), bool> with_promise = true>
Future<R> future_send_closure(ActorIdT &&actor_id, FunctionT function, ArgsT &&... args) {
auto pf = make_promise_future<R>();
send_closure(std::forward<ActorIdT>(actor_id), std::move(function), std::forward<ArgsT>(args)...,
std::move(pf.first));
return std::move(pf.second);
}
template <typename ActorIdT, typename FunctionT, typename... ArgsT> template <typename ActorIdT, typename FunctionT, typename... ArgsT>
bool send_closure_bool(ActorIdT &&actor_id, FunctionT function, ArgsT &&... args) { bool send_closure_bool(ActorIdT &&actor_id, FunctionT function, ArgsT &&... args) {
send_closure(std::forward<ActorIdT>(actor_id), function, std::forward<ArgsT>(args)...); send_closure(std::forward<ActorIdT>(actor_id), function, std::forward<ArgsT>(args)...);

View file

@ -63,6 +63,36 @@ class ActorSignals {
using core::Actor; using core::Actor;
using core::SchedulerContext; using core::SchedulerContext;
using core::SchedulerId; using core::SchedulerId;
using core::set_debug;
struct Debug {
public:
Debug() = default;
Debug(std::shared_ptr<core::SchedulerGroupInfo> group_info) : group_info_(std::move(group_info)) {
}
template <class F>
void for_each(F &&f) {
for (auto &scheduler : group_info_->schedulers) {
f(scheduler.io_worker->debug);
for (auto &cpu : scheduler.cpu_workers) {
f(cpu->debug);
}
}
}
void dump() {
for_each([](core::Debug &debug) {
core::DebugInfo info;
debug.read(info);
if (info.is_active) {
LOG(ERROR) << info.name << " " << td::format::as_time(Time::now() - info.start_at);
}
});
}
private:
std::shared_ptr<core::SchedulerGroupInfo> group_info_;
};
class Scheduler { class Scheduler {
public: public:
@ -110,6 +140,10 @@ class Scheduler {
} }
} }
Debug get_debug() {
return Debug{group_info_};
}
bool run() { bool run() {
start(); start();
while (schedulers_[0]->run(10)) { while (schedulers_[0]->run(10)) {

View file

@ -32,6 +32,7 @@ void CpuWorker::run() {
MpmcWaiter::Slot slot; MpmcWaiter::Slot slot;
waiter_.init_slot(slot, thread_id); waiter_.init_slot(slot, thread_id);
auto &debug = dispatcher.get_debug();
while (true) { while (true) {
SchedulerMessage message; SchedulerMessage message;
if (try_pop(message, thread_id)) { if (try_pop(message, thread_id)) {
@ -39,6 +40,7 @@ void CpuWorker::run() {
if (!message) { if (!message) {
return; return;
} }
auto lock = debug.start(message->get_name());
ActorExecutor executor(*message, dispatcher, ActorExecutor::Options().with_from_queue()); ActorExecutor executor(*message, dispatcher, ActorExecutor::Options().with_from_queue());
} else { } else {
waiter_.wait(slot); waiter_.wait(slot);

View file

@ -19,6 +19,7 @@
#include "td/actor/core/IoWorker.h" #include "td/actor/core/IoWorker.h"
#include "td/actor/core/ActorExecutor.h" #include "td/actor/core/ActorExecutor.h"
#include "td/actor/core/Scheduler.h"
namespace td { namespace td {
namespace actor { namespace actor {
@ -42,6 +43,7 @@ bool IoWorker::run_once(double timeout) {
auto &poll = SchedulerContext::get()->get_poll(); auto &poll = SchedulerContext::get()->get_poll();
#endif #endif
auto &heap = SchedulerContext::get()->get_heap(); auto &heap = SchedulerContext::get()->get_heap();
auto &debug = SchedulerContext::get()->get_debug();
auto now = Time::now(); // update Time::now_cached() auto now = Time::now(); // update Time::now_cached()
while (!heap.empty() && heap.top_key() <= now) { while (!heap.empty() && heap.top_key() <= now) {
@ -49,6 +51,7 @@ bool IoWorker::run_once(double timeout) {
auto *actor_info = ActorInfo::from_heap_node(heap_node); auto *actor_info = ActorInfo::from_heap_node(heap_node);
auto id = actor_info->unpin(); auto id = actor_info->unpin();
auto lock = debug.start(actor_info->get_name());
ActorExecutor executor(*actor_info, dispatcher, ActorExecutor::Options().with_has_poll(true)); ActorExecutor executor(*actor_info, dispatcher, ActorExecutor::Options().with_has_poll(true));
if (executor.can_send_immediate()) { if (executor.can_send_immediate()) {
executor.send_immediate(ActorSignals::one(ActorSignals::Alarm)); executor.send_immediate(ActorSignals::one(ActorSignals::Alarm));
@ -68,6 +71,7 @@ bool IoWorker::run_once(double timeout) {
dispatcher.set_alarm_timestamp(message); dispatcher.set_alarm_timestamp(message);
continue; continue;
} }
auto lock = debug.start(message->get_name());
ActorExecutor executor(*message, dispatcher, ActorExecutor::Options().with_from_queue().with_has_poll(true)); ActorExecutor executor(*message, dispatcher, ActorExecutor::Options().with_from_queue().with_has_poll(true));
} }
queue_.reader_flush(); queue_.reader_flush();

View file

@ -25,6 +25,15 @@ namespace td {
namespace actor { namespace actor {
namespace core { namespace core {
std::atomic<bool> debug;
void set_debug(bool flag) {
debug = flag;
}
bool need_debug() {
return debug.load(std::memory_order_relaxed);
}
Scheduler::Scheduler(std::shared_ptr<SchedulerGroupInfo> scheduler_group_info, SchedulerId id, size_t cpu_threads_count) Scheduler::Scheduler(std::shared_ptr<SchedulerGroupInfo> scheduler_group_info, SchedulerId id, size_t cpu_threads_count)
: scheduler_group_info_(std::move(scheduler_group_info)), cpu_threads_(cpu_threads_count) { : scheduler_group_info_(std::move(scheduler_group_info)), cpu_threads_(cpu_threads_count) {
scheduler_group_info_->active_scheduler_count++; scheduler_group_info_->active_scheduler_count++;
@ -128,13 +137,14 @@ void Scheduler::do_stop() {
} }
Scheduler::ContextImpl::ContextImpl(ActorInfoCreator *creator, SchedulerId scheduler_id, CpuWorkerId cpu_worker_id, Scheduler::ContextImpl::ContextImpl(ActorInfoCreator *creator, SchedulerId scheduler_id, CpuWorkerId cpu_worker_id,
SchedulerGroupInfo *scheduler_group, Poll *poll, KHeap<double> *heap) SchedulerGroupInfo *scheduler_group, Poll *poll, KHeap<double> *heap, Debug *debug)
: creator_(creator) : creator_(creator)
, scheduler_id_(scheduler_id) , scheduler_id_(scheduler_id)
, cpu_worker_id_(cpu_worker_id) , cpu_worker_id_(cpu_worker_id)
, scheduler_group_(scheduler_group) , scheduler_group_(scheduler_group)
, poll_(poll) , poll_(poll)
, heap_(heap) { , heap_(heap)
, debug_(debug) {
} }
SchedulerId Scheduler::ContextImpl::get_scheduler_id() const { SchedulerId Scheduler::ContextImpl::get_scheduler_id() const {
@ -184,6 +194,9 @@ KHeap<double> &Scheduler::ContextImpl::get_heap() {
CHECK(has_heap()); CHECK(has_heap());
return *heap_; return *heap_;
} }
Debug &Scheduler::ContextImpl::get_debug() {
return *debug_;
}
void Scheduler::ContextImpl::set_alarm_timestamp(const ActorInfoPtr &actor_info_ptr) { void Scheduler::ContextImpl::set_alarm_timestamp(const ActorInfoPtr &actor_info_ptr) {
// Ideas for optimization // Ideas for optimization

View file

@ -31,6 +31,7 @@
#include "td/actor/core/SchedulerId.h" #include "td/actor/core/SchedulerId.h"
#include "td/actor/core/SchedulerMessage.h" #include "td/actor/core/SchedulerMessage.h"
#include "td/utils/AtomicRead.h"
#include "td/utils/Closure.h" #include "td/utils/Closure.h"
#include "td/utils/common.h" #include "td/utils/common.h"
#include "td/utils/format.h" #include "td/utils/format.h"
@ -65,6 +66,54 @@ namespace actor {
namespace core { namespace core {
class IoWorker; class IoWorker;
struct DebugInfo {
bool is_active{false};
double start_at{0};
static constexpr size_t name_size{32};
char name[name_size] = {};
void set_name(td::Slice from) {
from.truncate(name_size - 1);
std::memcpy(name, from.data(), from.size());
name[from.size()] = 0;
}
};
void set_debug(bool flag);
bool need_debug();
struct Debug {
public:
bool is_on() const {
return need_debug();
}
struct Destructor {
void operator()(Debug *info) {
info->info_.lock().value().is_active = false;
}
};
void read(DebugInfo &info) {
info_.read(info);
}
std::unique_ptr<Debug, Destructor> start(td::Slice name) {
if (!is_on()) {
return {};
}
{
auto lock = info_.lock();
auto &value = lock.value();
value.is_active = true;
value.start_at = Time::now();
value.set_name(name);
}
return std::unique_ptr<Debug, Destructor>(this);
}
private:
AtomicRead<DebugInfo> info_;
};
struct WorkerInfo { struct WorkerInfo {
enum class Type { Io, Cpu } type{Type::Io}; enum class Type { Io, Cpu } type{Type::Io};
WorkerInfo() = default; WorkerInfo() = default;
@ -73,6 +122,7 @@ struct WorkerInfo {
} }
ActorInfoCreator actor_info_creator; ActorInfoCreator actor_info_creator;
CpuWorkerId cpu_worker_id; CpuWorkerId cpu_worker_id;
Debug debug;
}; };
template <class T> template <class T>
@ -195,7 +245,7 @@ class Scheduler {
class ContextImpl : public SchedulerContext { class ContextImpl : public SchedulerContext {
public: public:
ContextImpl(ActorInfoCreator *creator, SchedulerId scheduler_id, CpuWorkerId cpu_worker_id, ContextImpl(ActorInfoCreator *creator, SchedulerId scheduler_id, CpuWorkerId cpu_worker_id,
SchedulerGroupInfo *scheduler_group, Poll *poll, KHeap<double> *heap); SchedulerGroupInfo *scheduler_group, Poll *poll, KHeap<double> *heap, Debug *debug);
SchedulerId get_scheduler_id() const override; SchedulerId get_scheduler_id() const override;
void add_to_queue(ActorInfoPtr actor_info_ptr, SchedulerId scheduler_id, bool need_poll) override; void add_to_queue(ActorInfoPtr actor_info_ptr, SchedulerId scheduler_id, bool need_poll) override;
@ -208,6 +258,8 @@ class Scheduler {
bool has_heap() override; bool has_heap() override;
KHeap<double> &get_heap() override; KHeap<double> &get_heap() override;
Debug &get_debug() override;
void set_alarm_timestamp(const ActorInfoPtr &actor_info_ptr) override; void set_alarm_timestamp(const ActorInfoPtr &actor_info_ptr) override;
bool is_stop_requested() override; bool is_stop_requested() override;
@ -225,6 +277,8 @@ class Scheduler {
Poll *poll_; Poll *poll_;
KHeap<double> *heap_; KHeap<double> *heap_;
Debug *debug_;
}; };
template <class F> template <class F>
@ -234,7 +288,8 @@ class Scheduler {
#endif #endif
bool is_io_worker = worker_info.type == WorkerInfo::Type::Io; bool is_io_worker = worker_info.type == WorkerInfo::Type::Io;
ContextImpl context(&worker_info.actor_info_creator, info_->id, worker_info.cpu_worker_id, ContextImpl context(&worker_info.actor_info_creator, info_->id, worker_info.cpu_worker_id,
scheduler_group_info_.get(), is_io_worker ? &poll_ : nullptr, is_io_worker ? &heap_ : nullptr); scheduler_group_info_.get(), is_io_worker ? &poll_ : nullptr, is_io_worker ? &heap_ : nullptr,
&worker_info.debug);
SchedulerContext::Guard guard(&context); SchedulerContext::Guard guard(&context);
f(); f();
} }

View file

@ -37,6 +37,7 @@ class SchedulerDispatcher {
virtual void set_alarm_timestamp(const ActorInfoPtr &actor_info_ptr) = 0; virtual void set_alarm_timestamp(const ActorInfoPtr &actor_info_ptr) = 0;
}; };
struct Debug;
class SchedulerContext : public Context<SchedulerContext>, public SchedulerDispatcher { class SchedulerContext : public Context<SchedulerContext>, public SchedulerDispatcher {
public: public:
virtual ~SchedulerContext() = default; virtual ~SchedulerContext() = default;
@ -55,6 +56,9 @@ class SchedulerContext : public Context<SchedulerContext>, public SchedulerDispa
// Stop all schedulers // Stop all schedulers
virtual bool is_stop_requested() = 0; virtual bool is_stop_requested() = 0;
virtual void stop() = 0; virtual void stop() = 0;
// Debug
virtual Debug &get_debug() = 0;
}; };
} // namespace core } // namespace core
} // namespace actor } // namespace actor

View file

@ -675,7 +675,8 @@ TEST(Actor2, actor_function_result) {
public: public:
A(std::shared_ptr<td::Destructor> watcher) : watcher_(std::move(watcher)) { A(std::shared_ptr<td::Destructor> watcher) : watcher_(std::move(watcher)) {
} }
void on_result(uint32 x, uint32 y) { void on_result(uint32 x, td::Result<uint32> r_y) {
auto y = r_y.move_as_ok();
LOG_CHECK(x * x == y) << x << " " << y; LOG_CHECK(x * x == y) << x << " " << y;
if (--cnt_ == 0) { if (--cnt_ == 0) {
stop(); stop();
@ -683,7 +684,7 @@ TEST(Actor2, actor_function_result) {
} }
void start_up() { void start_up() {
b_ = create_actor<B>(ActorOptions().with_name("B")); b_ = create_actor<B>(ActorOptions().with_name("B"));
cnt_ = 3; cnt_ = 5;
send_closure(b_, &B::query, 3, [a = std::make_unique<int>(), self = actor_id(this)](td::Result<uint32> y) { send_closure(b_, &B::query, 3, [a = std::make_unique<int>(), self = actor_id(this)](td::Result<uint32> y) {
LOG_IF(ERROR, y.is_error()) << y.error(); LOG_IF(ERROR, y.is_error()) << y.error();
send_closure(self, &A::on_result, 3, y.ok()); send_closure(self, &A::on_result, 3, y.ok());
@ -696,6 +697,11 @@ TEST(Actor2, actor_function_result) {
CHECK(!self.empty()); CHECK(!self.empty());
send_closure(self, &A::on_result, 5, y); send_closure(self, &A::on_result, 5, y);
}); });
auto future = future_send_closure(b_, &B::query, 7);
future.finish(td::promise_send_closure(actor_id(this), &A::on_result, 7));
//TODO: deduce Future type (i.e. Future<td::uint32>)
auto future2 = future_send_closure<td::uint32>(b_, &B::query_async, 7);
future2.finish(td::promise_send_closure(actor_id(this), &A::on_result, 7));
} }
private: private:
@ -714,12 +720,12 @@ TEST(Actor2, actor_function_result) {
} }
TEST(Actor2, actor_ping_pong) { TEST(Actor2, actor_ping_pong) {
auto group_info = std::make_shared<core::SchedulerGroupInfo>(1); Scheduler scheduler{{3}, Scheduler::Paused};
core::Scheduler scheduler{group_info, SchedulerId{0}, 3};
sb.clear(); sb.clear();
scheduler.start(); scheduler.start();
auto watcher = td::create_shared_destructor([] { SchedulerContext::get()->stop(); }); auto watcher = td::create_shared_destructor([] { SchedulerContext::get()->stop(); });
td::actor::set_debug(true);
for (int i = 0; i < 2000; i++) { for (int i = 0; i < 2000; i++) {
scheduler.run_in_context([watcher] { scheduler.run_in_context([watcher] {
class PingPong : public Actor { class PingPong : public Actor {
@ -781,9 +787,9 @@ TEST(Actor2, actor_ping_pong) {
}); });
} }
watcher.reset(); watcher.reset();
while (scheduler.run(1000)) { while (scheduler.run(0.1)) {
//scheduler.get_debug().dump();
} }
core::Scheduler::close_scheduler_group(*group_info);
sb.clear(); sb.clear();
} }

View file

@ -135,6 +135,78 @@ TEST(Actor, safe_promise) {
ASSERT_EQ(res, 3); ASSERT_EQ(res, 3);
} }
TEST(Actor, split_promise) {
using td::Promise;
using td::Result;
using td::split_promise;
using td::SplitPromise;
{
td::optional<std::pair<int, double>> x;
auto pair = [&](Result<std::pair<int, double>> res) { x = res.move_as_ok(); };
static_assert(std::is_same<SplitPromise<decltype(pair)>::ArgT, std::pair<int, double>>::value, "A");
static_assert(
std::is_same<SplitPromise<decltype(pair)>::SplittedT, std::pair<Promise<int>, Promise<double>>>::value, "A");
auto splitted = split_promise(pair);
static_assert(std::is_same<decltype(splitted), std::pair<Promise<int>, Promise<double>>>::value, "A");
splitted.first.set_value(1);
splitted.second.set_value(2.0);
CHECK(x.unwrap() == std::make_pair(1, 2.0));
} // namespace td
{
td::optional<std::tuple<int, double, std::string>> x;
auto triple = [&](Result<std::tuple<int, double, std::string>> res) { x = res.move_as_ok(); };
static_assert(std::is_same<SplitPromise<decltype(triple)>::ArgT, std::tuple<int, double, std::string>>::value, "A");
static_assert(std::is_same<SplitPromise<decltype(triple)>::SplittedT,
std::tuple<Promise<int>, Promise<double>, Promise<std::string>>>::value,
"A");
auto splitted = split_promise(triple);
static_assert(
std::is_same<decltype(splitted), std::tuple<Promise<int>, Promise<double>, Promise<std::string>>>::value, "A");
std::get<0>(splitted).set_value(1);
std::get<1>(splitted).set_value(2.0);
std::get<2>(splitted).set_value("hello");
CHECK(x.unwrap() == std::make_tuple(1, 2.0, "hello"));
}
{
int code = 0;
auto pair = [&](Result<std::pair<int, double>> res) {
res.ensure_error();
code = res.error().code();
};
auto splitted = split_promise(td::Promise<std::pair<int, double>>(pair));
splitted.second.set_error(td::Status::Error(123, "123"));
CHECK(code == 0);
splitted.first.set_value(1);
CHECK(code == 123);
}
}
TEST(Actor, promise_future) {
using td::make_promise_future;
{
auto pf = make_promise_future<int>();
td::optional<int> res;
pf.second.map([](int x) { return x * 2; }).map([](int x) { return x + 10; }).map([&](int x) {
res = x;
return td::Unit();
});
CHECK(!res);
pf.first.set_value(6);
ASSERT_EQ(22, res.unwrap());
}
{
LOG(ERROR) << "Second test";
td::optional<int> res;
td::make_future(6)
.map([](int x) { return x * 2; })
.map([](int x) { return x + 10; })
.fmap([&](int x) { return td::make_future(x * 2); })
.finish([&](int x) { res = x; });
ASSERT_EQ(44, res.unwrap());
}
}
TEST(Actor2, actor_lost_promise) { TEST(Actor2, actor_lost_promise) {
using namespace td::actor; using namespace td::actor;
using namespace td; using namespace td;
@ -459,7 +531,7 @@ class SampleActor : public Actor {
detail::current_actor<Printer>().print_a(); detail::current_actor<Printer>().print_a();
co_await OnActor(self); co_await OnActor(self);
LOG(ERROR) << "exit print_a"; LOG(ERROR) << "exit print_a";
co_return{}; co_return {};
} }
task<Unit> print_b() { task<Unit> print_b() {
auto self = actor_id(this); auto self = actor_id(this);
@ -468,7 +540,7 @@ class SampleActor : public Actor {
detail::current_actor<Printer>().print_b(); detail::current_actor<Printer>().print_b();
co_await OnActor(self); co_await OnActor(self);
LOG(ERROR) << "exit print_b"; LOG(ERROR) << "exit print_b";
co_return{}; co_return {};
} }
immediate_task run_coroutine() { immediate_task run_coroutine() {

View file

@ -55,6 +55,10 @@ class KeyValue : public KeyValueReader {
virtual Status set(Slice key, Slice value) = 0; virtual Status set(Slice key, Slice value) = 0;
virtual Status erase(Slice key) = 0; virtual Status erase(Slice key) = 0;
virtual Status begin_write_batch() = 0;
virtual Status commit_write_batch() = 0;
virtual Status abort_write_batch() = 0;
virtual Status begin_transaction() = 0; virtual Status begin_transaction() = 0;
virtual Status commit_transaction() = 0; virtual Status commit_transaction() = 0;
virtual Status abort_transaction() = 0; virtual Status abort_transaction() = 0;
@ -86,6 +90,16 @@ class PrefixedKeyValue : public KeyValue {
return kv_->erase(PSLICE() << prefix_ << key); return kv_->erase(PSLICE() << prefix_ << key);
} }
Status begin_write_batch() override {
return kv_->begin_write_batch();
}
Status commit_write_batch() override {
return kv_->commit_write_batch();
}
Status abort_write_batch() override {
return kv_->abort_write_batch();
}
Status begin_transaction() override { Status begin_transaction() override {
return kv_->begin_transaction(); return kv_->begin_transaction();
} }

View file

@ -61,6 +61,15 @@ std::unique_ptr<KeyValueReader> MemoryKeyValue::snapshot() {
std::string MemoryKeyValue::stats() const { std::string MemoryKeyValue::stats() const {
return PSTRING() << "MemoryKeyValueStats{" << tag("get_count", get_count_) << "}"; return PSTRING() << "MemoryKeyValueStats{" << tag("get_count", get_count_) << "}";
} }
Status MemoryKeyValue::begin_write_batch() {
UNREACHABLE();
}
Status MemoryKeyValue::commit_write_batch() {
UNREACHABLE();
}
Status MemoryKeyValue::abort_write_batch() {
UNREACHABLE();
}
Status MemoryKeyValue::begin_transaction() { Status MemoryKeyValue::begin_transaction() {
UNREACHABLE(); UNREACHABLE();

View file

@ -29,6 +29,10 @@ class MemoryKeyValue : public KeyValue {
Status erase(Slice key) override; Status erase(Slice key) override;
Result<size_t> count(Slice prefix) override; Result<size_t> count(Slice prefix) override;
Status begin_write_batch() override;
Status commit_write_batch() override;
Status abort_write_batch() override;
Status begin_transaction() override; Status begin_transaction() override;
Status commit_transaction() override; Status commit_transaction() override;
Status abort_transaction() override; Status abort_transaction() override;

View file

@ -78,7 +78,18 @@ Result<RocksDb> RocksDb::open(std::string path) {
options.bytes_per_sync = 1 << 20; options.bytes_per_sync = 1 << 20;
options.writable_file_max_buffer_size = 2 << 14; options.writable_file_max_buffer_size = 2 << 14;
options.statistics = statistics; options.statistics = statistics;
TRY_STATUS(from_rocksdb(rocksdb::OptimisticTransactionDB::Open(options, std::move(path), &db))); rocksdb::OptimisticTransactionDBOptions occ_options;
occ_options.validate_policy = rocksdb::OccValidationPolicy::kValidateSerial;
rocksdb::ColumnFamilyOptions cf_options(options);
std::vector<rocksdb::ColumnFamilyDescriptor> column_families;
column_families.push_back(rocksdb::ColumnFamilyDescriptor(rocksdb::kDefaultColumnFamilyName, cf_options));
std::vector<rocksdb::ColumnFamilyHandle *> handles;
TRY_STATUS(from_rocksdb(
rocksdb::OptimisticTransactionDB::Open(options, occ_options, std::move(path), column_families, &handles, &db)));
CHECK(handles.size() == 1);
// i can delete the handle since DBImpl is always holding a reference to
// default column family
delete handles[0];
} }
return RocksDb(std::shared_ptr<rocksdb::OptimisticTransactionDB>(db), std::move(statistics)); return RocksDb(std::shared_ptr<rocksdb::OptimisticTransactionDB>(db), std::move(statistics));
} }
@ -161,31 +172,41 @@ Result<size_t> RocksDb::count(Slice prefix) {
return res; return res;
} }
Status RocksDb::begin_write_batch() {
CHECK(!transaction_);
write_batch_ = std::make_unique<rocksdb::WriteBatch>();
return Status::OK();
}
Status RocksDb::begin_transaction() { Status RocksDb::begin_transaction() {
//write_batch_ = std::make_unique<rocksdb::WriteBatch>(); CHECK(!write_batch_);
rocksdb::WriteOptions options; rocksdb::WriteOptions options;
options.sync = true; options.sync = true;
transaction_.reset(db_->BeginTransaction(options, {})); transaction_.reset(db_->BeginTransaction(options, {}));
return Status::OK(); return Status::OK();
} }
Status RocksDb::commit_transaction() { Status RocksDb::commit_write_batch() {
//CHECK(write_batch_); CHECK(write_batch_);
//auto write_batch = std::move(write_batch_); auto write_batch = std::move(write_batch_);
//rocksdb::WriteOptions options; rocksdb::WriteOptions options;
//options.sync = true; options.sync = true;
//TRY_STATUS(from_rocksdb(db_->Write(options, write_batch.get()))); return from_rocksdb(db_->Write(options, write_batch.get()));
//return Status::OK(); }
Status RocksDb::commit_transaction() {
CHECK(transaction_); CHECK(transaction_);
auto res = from_rocksdb(transaction_->Commit()); auto transaction = std::move(transaction_);
transaction_.reset(); return from_rocksdb(transaction->Commit());
return res; }
Status RocksDb::abort_write_batch() {
CHECK(write_batch_);
write_batch_.reset();
return Status::OK();
} }
Status RocksDb::abort_transaction() { Status RocksDb::abort_transaction() {
//CHECK(write_batch_);
//write_batch_.reset();
CHECK(transaction_); CHECK(transaction_);
transaction_.reset(); transaction_.reset();
return Status::OK(); return Status::OK();

View file

@ -45,6 +45,10 @@ class RocksDb : public KeyValue {
Status erase(Slice key) override; Status erase(Slice key) override;
Result<size_t> count(Slice prefix) override; Result<size_t> count(Slice prefix) override;
Status begin_write_batch() override;
Status commit_write_batch() override;
Status abort_write_batch() override;
Status begin_transaction() override; Status begin_transaction() override;
Status commit_transaction() override; Status commit_transaction() override;
Status abort_transaction() override; Status abort_transaction() override;

View file

@ -0,0 +1,90 @@
/*
This file is part of TON Blockchain source code.
TON Blockchain is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
TON Blockchain is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
You must obey the GNU General Public License in all respects for all
of the code used other than OpenSSL. If you modify file(s) with this
exception, you may extend this exception to your version of the file(s),
but you are not obligated to do so. If you do not wish to do so, delete this
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2019-2020 Telegram Systems LLP
*/
#include <atomic>
#include "td/utils/common.h"
#include "td/utils/port/thread.h"
namespace td {
template <class T>
class AtomicRead {
public:
void read(T &dest) const {
while (true) {
static_assert(std::is_trivially_copyable<T>::value, "T must be trivially copyable");
auto version_before = version.load();
memcpy(&dest, &value, sizeof(dest));
auto version_after = version.load();
if (version_before == version_after && version_before % 2 == 0) {
break;
}
td::this_thread::yield();
}
}
friend struct Write;
struct Write {
explicit Write(AtomicRead *read) {
read->do_lock();
ptr.reset(read);
}
struct Destructor {
void operator()(AtomicRead *read) const {
read->do_unlock();
}
};
T &operator*() {
return value();
}
T *operator->() {
return &value();
}
T &value() {
CHECK(ptr);
return ptr->value;
}
private:
std::unique_ptr<AtomicRead, Destructor> ptr;
};
Write lock() {
return Write(this);
}
private:
std::atomic<td::uint64> version{0};
T value;
void do_lock() {
CHECK(++version % 2 == 1);
}
void do_unlock() {
CHECK(++version % 2 == 0);
}
};
}; // namespace td

View file

@ -57,6 +57,15 @@
} \ } \
} }
#define TRY_STATUS_PROMISE_PREFIX(promise_name, status, prefix) \
{ \
auto try_status = (status); \
if (try_status.is_error()) { \
promise_name.set_error(try_status.move_as_error_prefix(prefix)); \
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) \
@ -437,6 +446,7 @@ class Status {
template <class T = Unit> template <class T = Unit>
class Result { class Result {
public: public:
using ValueT = T;
Result() : status_(Status::Error<-1>()) { Result() : status_(Status::Error<-1>()) {
} }
template <class S, std::enable_if_t<!std::is_same<std::decay_t<S>, Result>::value, int> = 0> template <class S, std::enable_if_t<!std::is_same<std::decay_t<S>, Result>::value, int> = 0>

View file

@ -28,6 +28,7 @@
#include "td/utils/tests.h" #include "td/utils/tests.h"
#include "td/utils/benchmark.h" #include "td/utils/benchmark.h"
#include "td/utils/AtomicRead.h"
#include "td/utils/StealingQueue.h" #include "td/utils/StealingQueue.h"
#include "td/utils/MpmcQueue.h" #include "td/utils/MpmcQueue.h"
@ -79,6 +80,46 @@ TEST(AtomicRead, simple) {
thread.join(); thread.join();
} }
} }
TEST(AtomicRead, simple2) {
td::Stage run;
td::Stage check;
size_t threads_n = 10;
std::vector<td::thread> threads;
struct Value {
td::uint64 value = 0;
char str[50] = "0 0 0 0";
};
AtomicRead<Value> value;
auto to_str = [](size_t i) { return PSTRING() << i << " " << i << " " << i << " " << i; };
for (size_t i = 0; i < threads_n; i++) {
threads.push_back(td::thread([&, id = static_cast<uint32>(i)] {
for (uint64 round = 1; round < 10000; round++) {
if (id == 0) {
}
run.wait(round * threads_n);
if (id == 0) {
auto x = value.lock();
x->value = round;
auto str = to_str(round);
memcpy(x->str, str.c_str(), str.size() + 1);
} else {
Value x;
value.read(x);
LOG_CHECK(x.value == round || x.value == round - 1) << x.value << " " << round;
CHECK(x.str == to_str(x.value));
}
check.wait(round * threads_n);
}
}));
}
for (auto &thread : threads) {
thread.join();
}
}
TEST(StealingQueue, simple) { TEST(StealingQueue, simple) {
uint64 sum; uint64 sum;
std::atomic<uint64> got_sum; std::atomic<uint64> got_sum;

97
test/test-rocksdb.cpp Normal file
View file

@ -0,0 +1,97 @@
/*
This file is part of TON Blockchain source code.
TON Blockchain is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
TON Blockchain is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
You must obey the GNU General Public License in all respects for all
of the code used other than OpenSSL. If you modify file(s) with this
exception, you may extend this exception to your version of the file(s),
but you are not obligated to do so. If you do not wish to do so, delete this
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2017-2020 Telegram Systems LLP
*/
#include "td/db/RocksDb.h"
#include "td/utils/OptionsParser.h"
#include "td/utils/port/signals.h"
#include "td/utils/port/path.h"
#include "td/utils/StringBuilder.h"
#include <iostream>
std::string dir = "stress-db";
int db_n = 20;
int key_n = 1000000;
std::string get_db_path(int i) {
return PSTRING() << dir << TD_DIR_SLASH << "db-" << i;
}
void do_create_db() {
td::mkdir(dir).ensure();
for (int db_i = 0; db_i < db_n; db_i++) {
LOG(ERROR) << "db_i=" << db_i;
auto db = td::RocksDb::open(get_db_path(db_i)).move_as_ok();
for (int key_i = 0; key_i < key_n; key_i++) {
db.set(PSLICE() << key_i, PSLICE() << key_i);
}
}
}
void do_load_db() {
static std::vector<td::RocksDb> dbs;
for (int db_i = 0; db_i < db_n; db_i++) {
LOG(ERROR) << "db_i=" << db_i;
auto db = td::RocksDb::open(get_db_path(db_i)).move_as_ok();
for (int key_i = 0; key_i < key_n; key_i++) {
std::string value;
db.get(PSLICE() << key_i, value).ensure();
}
dbs.push_back(std::move(db));
}
}
int main(int argc, char *argv[]) {
SET_VERBOSITY_LEVEL(verbosity_DEBUG);
td::set_default_failure_signal_handler().ensure();
td::OptionsParser p;
p.set_description("test basic adnl functionality");
p.add_option('h', "help", "prints_help", [&]() {
char b[10240];
td::StringBuilder sb(td::MutableSlice{b, 10000});
sb << p;
std::cout << sb.as_cslice().c_str();
std::exit(2);
return td::Status::OK();
});
bool create_db = false;
p.add_option('c', "create", "create test db", [&] {
create_db = true;
return td::Status::OK();
});
auto res = p.run(argc, argv);
LOG_IF(FATAL, res.is_error()) << res.error();
if (create_db) {
do_create_db();
} else {
do_load_db();
}
return 0;
}

@ -1 +1 @@
Subproject commit 325fd7b042ff4ec34f7dd32e602cd81ad0e24b22 Subproject commit df3ea785d8c30a9503321a3d35ee7d35808f190d

2
third-party/rocksdb vendored

@ -1 +1 @@
Subproject commit d6f2ecf49c28fee225477d39e2a1535a87919afe Subproject commit 0915c99f01b46f50af8e02da8b6528156f584b7c

View file

@ -25,7 +25,7 @@ keyStoreTypeInMemory = KeyStoreType;
config config:string blockchain_name:string use_callbacks_for_network:Bool ignore_cache:Bool = Config; config config:string blockchain_name:string use_callbacks_for_network:Bool ignore_cache:Bool = Config;
options config:config keystore_type:KeyStoreType = Options; options config:config keystore_type:KeyStoreType = Options;
options.configInfo default_wallet_id:int64 = options.ConfigInfo; options.configInfo default_wallet_id:int64 default_rwallet_init_public_key:string = options.ConfigInfo;
options.info config_info:options.configInfo = options.Info; options.info config_info:options.configInfo = options.Info;
key public_key:string secret:secureBytes = Key; key public_key:string secret:secureBytes = Key;
@ -55,6 +55,8 @@ raw.message source:accountAddress destination:accountAddress value:int64 fwd_fee
raw.transaction utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 storage_fee:int64 other_fee:int64 in_msg:raw.message out_msgs:vector<raw.message> = raw.Transaction; raw.transaction utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 storage_fee:int64 other_fee:int64 in_msg:raw.message out_msgs:vector<raw.message> = raw.Transaction;
raw.transactions transactions:vector<raw.transaction> previous_transaction_id:internal.transactionId = raw.Transactions; raw.transactions transactions:vector<raw.transaction> previous_transaction_id:internal.transactionId = raw.Transactions;
pchan.config alice_public_key:string alice_address:accountAddress bob_public_key:string bob_address:accountAddress init_timeout:int32 close_timeout:int32 channel_id:int64 = pchan.Config;
raw.initialAccountState code:bytes data:bytes = InitialAccountState; raw.initialAccountState code:bytes data:bytes = InitialAccountState;
testGiver.initialAccountState = InitialAccountState; testGiver.initialAccountState = InitialAccountState;
testWallet.initialAccountState public_key:string = InitialAccountState; testWallet.initialAccountState public_key:string = InitialAccountState;
@ -62,7 +64,13 @@ wallet.initialAccountState public_key:string = InitialAccountState;
wallet.v3.initialAccountState public_key:string wallet_id:int64 = InitialAccountState; wallet.v3.initialAccountState public_key:string wallet_id:int64 = InitialAccountState;
wallet.highload.v1.initialAccountState public_key:string wallet_id:int64 = InitialAccountState; wallet.highload.v1.initialAccountState public_key:string wallet_id:int64 = InitialAccountState;
wallet.highload.v2.initialAccountState public_key:string wallet_id:int64 = InitialAccountState; wallet.highload.v2.initialAccountState public_key:string wallet_id:int64 = InitialAccountState;
rwallet.limit seconds:int32 value:int64 = rwallet.Limit;
rwallet.config start_at:int53 limits:vector<rwallet.limit> = rwallet.Config;
rwallet.initialAccountState init_public_key:string public_key:string wallet_id:int64 = InitialAccountState;
dns.initialAccountState public_key:string wallet_id:int64 = InitialAccountState; dns.initialAccountState public_key:string wallet_id:int64 = InitialAccountState;
pchan.initialAccountState config:pchan.config = InitialAccountState;
raw.accountState code:bytes data:bytes frozen_hash:bytes = AccountState; raw.accountState code:bytes data:bytes frozen_hash:bytes = AccountState;
testWallet.accountState seqno:int32 = AccountState; testWallet.accountState seqno:int32 = AccountState;
@ -72,6 +80,13 @@ wallet.highload.v1.accountState wallet_id:int64 seqno:int32 = AccountState;
wallet.highload.v2.accountState wallet_id:int64 = AccountState; wallet.highload.v2.accountState wallet_id:int64 = AccountState;
testGiver.accountState seqno:int32 = AccountState; testGiver.accountState seqno:int32 = AccountState;
dns.accountState wallet_id:int64 = AccountState; dns.accountState wallet_id:int64 = AccountState;
rwallet.accountState wallet_id:int64 seqno:int32 unlocked_balance:int64 config:rwallet.config = AccountState;
pchan.stateInit signed_A:Bool signed_B:Bool min_A:int64 min_B:int64 expire_at:int53 A:int64 B:int64 = pchan.State;
pchan.stateClose signed_A:Bool signed_B:Bool min_A:int64 min_B:int64 expire_at:int53 A:int64 B:int64 = pchan.State;
pchan.statePayout A:int64 B:int64 = pchan.State;
pchan.accountState config:pchan.config state:pchan.State description:string = AccountState;
uninited.accountState frozen_hash:bytes = AccountState; uninited.accountState frozen_hash:bytes = AccountState;
fullAccountState balance:int64 last_transaction_id:internal.transactionId block_id:ton.blockIdExt sync_utime:int53 account_state:AccountState = FullAccountState; fullAccountState balance:int64 last_transaction_id:internal.transactionId block_id:ton.blockIdExt sync_utime:int53 account_state:AccountState = FullAccountState;
@ -83,7 +98,7 @@ syncStateInProgress from_seqno:int32 to_seqno:int32 current_seqno:int32 = SyncSt
// MSG // MSG
// //
msg.dataRaw body:bytes = msg.Data; msg.dataRaw body:bytes init_state:bytes = msg.Data;
msg.dataText text:bytes = msg.Data; msg.dataText text:bytes = msg.Data;
msg.dataDecryptedText text:bytes = msg.Data; msg.dataDecryptedText text:bytes = msg.Data;
msg.dataEncryptedText text:bytes = msg.Data; msg.dataEncryptedText text:bytes = msg.Data;
@ -115,6 +130,21 @@ dns.actionSet entry:dns.entry = dns.Action;
dns.resolved entries:vector<dns.entry> = dns.Resolved; dns.resolved entries:vector<dns.entry> = dns.Resolved;
//
// Payment channel
//
pchan.promise signature:bytes promise_A:int64 promise_B:int64 channel_id:int64 = pchan.Promise;
pchan.actionInit inc_A:int64 inc_B:int64 min_A:int64 min_B:int64 = pchan.Action;
pchan.actionClose extra_A:int64 extra_B:int64 promise:pchan.promise = pchan.Action;
pchan.actionTimeout = pchan.Action;
//
// Restricted wallet initialization
//
rwallet.actionInit config:rwallet.config = rwallet.Action;
// //
// Actions // Actions
// //
@ -122,12 +152,14 @@ dns.resolved entries:vector<dns.entry> = dns.Resolved;
actionNoop = Action; actionNoop = Action;
actionMsg messages:vector<msg.message> allow_send_to_uninited:Bool = Action; actionMsg messages:vector<msg.message> allow_send_to_uninited:Bool = Action;
actionDns actions:vector<dns.Action> = Action; actionDns actions:vector<dns.Action> = Action;
actionPchan action:pchan.Action = Action;
actionRwallet action:rwallet.actionInit = Action;
//actionMultisig actions:vector<multisig.order> = Action; //actionMultisig actions:vector<multisig.order> = Action;
fees in_fwd_fee:int53 storage_fee:int53 gas_fee:int53 fwd_fee:int53 = Fees; fees in_fwd_fee:int53 storage_fee:int53 gas_fee:int53 fwd_fee:int53 = Fees;
query.fees source_fees:fees destination_fees:vector<fees> = query.Fees; query.fees source_fees:fees destination_fees:vector<fees> = query.Fees;
// query.emulationResult exit_code:int32 fees:fees = query.EmulationResult; // query.emulationResult exit_code:int32 fees:fees = query.EmulationResult;
query.info id:int53 valid_until:int53 body_hash:bytes = query.Info; query.info id:int53 valid_until:int53 body_hash:bytes body:bytes init_state:bytes = query.Info;
tvm.slice bytes:bytes = tvm.Slice; tvm.slice bytes:bytes = tvm.Slice;
tvm.cell bytes:bytes = tvm.Cell; tvm.cell bytes:bytes = tvm.Cell;
@ -218,7 +250,7 @@ sync = ton.BlockIdExt;
getAccountAddress initial_account_state:InitialAccountState revision:int32 = AccountAddress; getAccountAddress initial_account_state:InitialAccountState revision:int32 = AccountAddress;
guessAccountRevision initial_account_state:InitialAccountState = AccountRevisionList; guessAccountRevision initial_account_state:InitialAccountState = AccountRevisionList;
getAccountState account_address:accountAddress = FullAccountState; getAccountState account_address:accountAddress = FullAccountState;
createQuery private_key:InputKey address:accountAddress timeout:int32 action:Action = query.Info; createQuery private_key:InputKey address:accountAddress timeout:int32 action:Action initial_account_state:InitialAccountState = query.Info;
msg.decrypt input_key:InputKey data:msg.dataEncryptedArray = msg.DataDecryptedArray; msg.decrypt input_key:InputKey data:msg.dataEncryptedArray = msg.DataDecryptedArray;
msg.decryptWithProof proof:bytes data:msg.dataEncrypted = msg.Data; msg.decryptWithProof proof:bytes data:msg.dataEncrypted = msg.Data;
@ -238,6 +270,12 @@ smc.runGetMethod id:int53 method:smc.MethodId stack:vector<tvm.StackEntry> = smc
dns.resolve account_address:accountAddress name:string category:int32 ttl:int32 = dns.Resolved; dns.resolve account_address:accountAddress name:string category:int32 ttl:int32 = dns.Resolved;
pchan.signPromise input_key:InputKey promise:pchan.promise = pchan.Promise;
pchan.validatePromise public_key:bytes promise:pchan.promise = Ok;
pchan.packPromise promise:pchan.promise = Data;
pchan.unpackPromise data:secureBytes = pchan.Promise;
onLiteServerQueryResult id:int64 bytes:bytes = Ok; onLiteServerQueryResult id:int64 bytes:bytes = Ok;
onLiteServerQueryError id:int64 error:error = Ok; onLiteServerQueryError id:int64 error:error = Ok;

Binary file not shown.

View file

@ -121,11 +121,12 @@ struct TransactionId {
}; };
struct AccountState { struct AccountState {
enum Type { Empty, Wallet, Dns, Unknown } type{Empty}; enum Type { Empty, Wallet, Dns, Pchan, Unknown } type{Empty};
td::int64 sync_utime{-1}; td::int64 sync_utime{-1};
td::int64 balance{-1}; td::int64 balance{-1};
TransactionId last_transaction_id; TransactionId last_transaction_id;
std::string address; std::string address;
tonlib_api::object_ptr<tonlib_api::fullAccountState> state;
bool is_inited() const { bool is_inited() const {
return type != Empty; return type != Empty;
@ -187,6 +188,10 @@ AccountState get_account_state(Client& client, std::string address) {
case tonlib_api::dns_accountState::ID: case tonlib_api::dns_accountState::ID:
res.type = AccountState::Dns; res.type = AccountState::Dns;
break; break;
case tonlib_api::pchan_accountState::ID:
res.type = AccountState::Pchan;
res.state = std::move(state);
break;
default: default:
res.type = AccountState::Unknown; res.type = AccountState::Unknown;
break; break;
@ -228,15 +233,38 @@ struct QueryInfo {
std::string body_hash; std::string body_hash;
}; };
struct Message {
bool encrypted = false;
td::optional<std::string> text;
td::optional<td::string> raw;
td::optional<td::string> init_state;
static Message create_text(std::string text, bool encrypted) {
Message res;
res.text = text;
res.encrypted = encrypted;
return res;
}
static Message create_raw(std::string raw, std::string init_state) {
Message res;
res.raw = raw;
res.init_state = init_state;
return res;
}
};
td::Result<QueryId> create_send_grams_query(Client& client, const Wallet& source, std::string destination, td::Result<QueryId> create_send_grams_query(Client& client, const Wallet& source, std::string destination,
td::int64 amount, bool encrypted, std::string message, bool force = false, td::int64 amount, Message message, bool force = false, int timeout = 0,
int timeout = 0, bool fake = false) { bool fake = false) {
std::vector<tonlib_api::object_ptr<tonlib_api::msg_message>> msgs; std::vector<tonlib_api::object_ptr<tonlib_api::msg_message>> msgs;
tonlib_api::object_ptr<tonlib_api::msg_Data> data; tonlib_api::object_ptr<tonlib_api::msg_Data> data;
if (encrypted) { if (message.text) {
data = tonlib_api::make_object<tonlib_api::msg_dataDecryptedText>(std::move(message)); if (message.encrypted) {
data = tonlib_api::make_object<tonlib_api::msg_dataDecryptedText>(message.text.unwrap());
} else { } else {
data = tonlib_api::make_object<tonlib_api::msg_dataText>(std::move(message)); data = tonlib_api::make_object<tonlib_api::msg_dataText>(message.text.unwrap());
}
} else {
data = tonlib_api::make_object<tonlib_api::msg_dataRaw>(message.raw.unwrap(), message.init_state.unwrap());
} }
msgs.push_back(tonlib_api::make_object<tonlib_api::msg_message>( msgs.push_back(tonlib_api::make_object<tonlib_api::msg_message>(
tonlib_api::make_object<tonlib_api::accountAddress>(destination), "", amount, std::move(data))); tonlib_api::make_object<tonlib_api::accountAddress>(destination), "", amount, std::move(data)));
@ -244,7 +272,7 @@ td::Result<QueryId> create_send_grams_query(Client& client, const Wallet& source
auto r_id = auto r_id =
sync_send(client, tonlib_api::make_object<tonlib_api::createQuery>( sync_send(client, tonlib_api::make_object<tonlib_api::createQuery>(
fake ? source.key.get_fake_input_key() : source.key.get_input_key(), source.get_address(), fake ? source.key.get_fake_input_key() : source.key.get_input_key(), source.get_address(),
timeout, tonlib_api::make_object<tonlib_api::actionMsg>(std::move(msgs), force))); timeout, tonlib_api::make_object<tonlib_api::actionMsg>(std::move(msgs), force), nullptr));
TRY_RESULT(id, std::move(r_id)); TRY_RESULT(id, std::move(r_id));
return QueryId{id->id_}; return QueryId{id->id_};
} }
@ -253,7 +281,7 @@ td::Result<QueryId> create_update_dns_query(Client& client, const Wallet& dns,
std::vector<tonlib_api::object_ptr<tonlib_api::dns_Action>> entries) { std::vector<tonlib_api::object_ptr<tonlib_api::dns_Action>> entries) {
using namespace ton::tonlib_api; using namespace ton::tonlib_api;
auto r_id = sync_send(client, make_object<createQuery>(dns.key.get_input_key(), dns.get_address(), 60, auto r_id = sync_send(client, make_object<createQuery>(dns.key.get_input_key(), dns.get_address(), 60,
make_object<actionDns>(std::move(entries)))); make_object<actionDns>(std::move(entries)), nullptr));
TRY_RESULT(id, std::move(r_id)); TRY_RESULT(id, std::move(r_id));
return QueryId{id->id_}; return QueryId{id->id_};
} }
@ -289,7 +317,7 @@ td::Result<AccountState> wait_state_change(Client& client, const AccountState& o
while (true) { while (true) {
auto new_state = get_account_state(client, old_state.address); auto new_state = get_account_state(client, old_state.address);
if (new_state.last_transaction_id.lt != old_state.last_transaction_id.lt) { if (new_state.last_transaction_id.lt != old_state.last_transaction_id.lt) {
return new_state; return std::move(new_state);
} }
if (valid_until != 0 && new_state.sync_utime >= valid_until) { if (valid_until != 0 && new_state.sync_utime >= valid_until) {
return td::Status::Error("valid_until expired"); return td::Status::Error("valid_until expired");
@ -325,18 +353,19 @@ td::Status transfer_grams(Client& client, const Wallet& wallet, std::string addr
LOG(INFO) << "Transfer: create query " << (double)amount / Gramm << " from " << wallet.address << " to " << address; LOG(INFO) << "Transfer: create query " << (double)amount / Gramm << " from " << wallet.address << " to " << address;
bool encrypt = true; bool encrypt = true;
auto r_query_id = create_send_grams_query(client, wallet, address, amount, encrypt, message, fast); auto r_query_id =
create_send_grams_query(client, wallet, address, amount, Message::create_text(message, encrypt), fast);
if (r_query_id.is_error()) { if (r_query_id.is_error()) {
LOG(INFO) << "Send query WITHOUT message encryption " << r_query_id.error(); LOG(INFO) << "Send query WITHOUT message encryption " << r_query_id.error();
encrypt = false; encrypt = false;
r_query_id = create_send_grams_query(client, wallet, address, amount, encrypt, message, fast); r_query_id = create_send_grams_query(client, wallet, address, amount, Message::create_text(message, encrypt), fast);
} else { } else {
LOG(INFO) << "Send query WITH message encryption"; LOG(INFO) << "Send query WITH message encryption";
} }
if (r_query_id.is_error() && td::begins_with(r_query_id.error().message(), "DANGEROUS_TRANSACTION")) { if (r_query_id.is_error() && td::begins_with(r_query_id.error().message(), "DANGEROUS_TRANSACTION")) {
ASSERT_TRUE(dst_state.type == AccountState::Empty); ASSERT_TRUE(dst_state.type == AccountState::Empty);
LOG(INFO) << "Transfer: recreate query due to DANGEROUS_TRANSACTION error"; LOG(INFO) << "Transfer: recreate query due to DANGEROUS_TRANSACTION error";
r_query_id = create_send_grams_query(client, wallet, address, amount, false, message, true); r_query_id = create_send_grams_query(client, wallet, address, amount, Message::create_text(message, encrypt), true);
} }
r_query_id.ensure(); r_query_id.ensure();
@ -458,8 +487,9 @@ Wallet create_empty_dns(Client& client) {
void test_estimate_fees_without_key(Client& client, const Wallet& wallet_a, const Wallet& wallet_b) { void test_estimate_fees_without_key(Client& client, const Wallet& wallet_a, const Wallet& wallet_b) {
LOG(ERROR) << " SUBTEST: estimate fees without key"; LOG(ERROR) << " SUBTEST: estimate fees without key";
{ {
auto query_id = auto query_id = create_send_grams_query(client, wallet_a, wallet_b.address, 0, Message::create_text("???", false),
create_send_grams_query(client, wallet_a, wallet_b.address, 0, false, "???", true, 0, true).move_as_ok(); true, 0, true)
.move_as_ok();
auto fees1 = query_estimate_fees(client, query_id, false); auto fees1 = query_estimate_fees(client, query_id, false);
auto fees2 = query_estimate_fees(client, query_id, true); auto fees2 = query_estimate_fees(client, query_id, true);
LOG(INFO) << "Fee without ignore_chksig\t" << fees1; LOG(INFO) << "Fee without ignore_chksig\t" << fees1;
@ -564,6 +594,141 @@ void dns_resolve(Client& client, const Wallet& dns, std::string name) {
LOG(INFO) << "OK"; LOG(INFO) << "OK";
} }
void test_paychan(Client& client, const Wallet& giver_wallet) {
LOG(INFO) << "Start test paychan";
auto alice = create_empty_wallet(client);
auto bob = create_empty_wallet(client);
using namespace ton::tonlib_api;
int init_timeout = 10;
int close_timeout = 10;
int64 channel_id = static_cast<td::int64>(td::Random::fast_uint64());
auto get_initial_state = [&] {
return make_object<pchan_initialAccountState>(make_object<pchan_config>(alice.key.public_key, alice.get_address(),
bob.key.public_key, bob.get_address(),
init_timeout, close_timeout, channel_id));
};
auto account_address = sync_send(client, make_object<tonlib_api::getAccountAddress>(get_initial_state(), -1))
.move_as_ok()
->account_address_;
auto get_account_address = [&] { return make_object<accountAddress>(account_address); };
//pchan.actionInit inc_A:int64 inc_B:int64 min_A:int64 min_B:int64 = pchan.Action;
//pchan.actionClose extra_A:int64 extra_B:int64 promise:pchan.promise = pchan.Action;
//pchan.actionTimeout = pchan.Action;
auto create_init_query = [&](auto& wallet, td::int64 inc_A, td::int64 inc_B) {
auto action = make_object<tonlib_api::actionPchan>(make_object<tonlib_api::pchan_actionInit>(inc_A, inc_B, 0, 0));
auto r_id = sync_send(client, make_object<createQuery>(wallet.key.get_input_key(), get_account_address(), 60,
std::move(action), get_initial_state()));
r_id.ensure();
return r_id.move_as_ok();
};
auto send_query_via = [&](auto& wallet, auto query_info, auto destination, td::int64 value) {
auto transfer_id =
create_send_grams_query(client, wallet, std::move(destination), value,
Message::create_raw(query_info->body_, query_info->init_state_), true, 60)
.move_as_ok();
::query_send(client, transfer_id);
};
send_query_via(giver_wallet, create_init_query(alice, 1 * Gramm, 0), account_address, 11 * Gramm / 10);
send_query_via(giver_wallet, create_init_query(bob, 0, 1 * Gramm), account_address, 11 * Gramm / 10);
LOG(INFO) << "Wait till pchan " << account_address << " is inited ";
td::optional<td::int64> now;
while (true) {
client.receive(1);
auto state = get_account_state(client, account_address);
if (!now) {
now = state.sync_utime;
}
CHECK(now.value() + 60 > state.sync_utime);
if (state.type != ::AccountState::Pchan) {
continue;
}
auto pchan_state = tonlib_api::move_object_as<tonlib_api::pchan_accountState>(state.state->account_state_);
if (pchan_state->state_->get_id() != tonlib_api::pchan_stateClose::ID) {
continue;
}
LOG(INFO) << "Account type: " << state.type << " " << state.sync_utime << "\n" << to_string(pchan_state->state_);
break;
}
auto create_close_query = [&](auto& x_wallet, auto& y_wallet, td::int64 A, td::int64 B) {
auto p = sync_send(client, make_object<pchan_signPromise>(y_wallet.key.get_input_key(),
make_object<pchan_promise>("", A, B, channel_id)))
.move_as_ok();
auto action = make_object<tonlib_api::actionPchan>(make_object<tonlib_api::pchan_actionClose>(0, 0, std::move(p)));
auto r_id = sync_send(client, make_object<createQuery>(x_wallet.key.get_input_key(), get_account_address(), 60,
std::move(action), get_initial_state()));
r_id.ensure();
return r_id.move_as_ok();
};
//auto send_query_ext = [&](auto query_info) { ::query_send(client, QueryId{query_info->id_}); };
send_query_via(giver_wallet, create_close_query(alice, bob, 0, 10 * Gramm), account_address, Gramm / 10);
send_query_via(giver_wallet, create_close_query(bob, alice, 11 * Gramm, 0), account_address, Gramm / 10);
LOG(INFO) << "Wait till pchan " << account_address << " is closed ";
now = {};
int64 payout_A = 0;
int64 payout_B = 0;
while (true) {
client.receive(1);
auto state = get_account_state(client, account_address);
if (!now) {
now = state.sync_utime;
}
CHECK(now.value() + 60 > state.sync_utime);
if (state.type != ::AccountState::Pchan) {
continue;
}
auto pchan_state = tonlib_api::move_object_as<tonlib_api::pchan_accountState>(state.state->account_state_);
if (pchan_state->state_->get_id() != tonlib_api::pchan_statePayout::ID) {
continue;
}
LOG(INFO) << "Account type: " << state.type << " " << state.sync_utime << "\n" << to_string(pchan_state->state_);
auto payout = tonlib_api::move_object_as<tonlib_api::pchan_statePayout>(pchan_state->state_);
payout_A = payout->A_;
payout_B = payout->B_;
break;
}
LOG(INFO) << "Wait till Alice has its share";
now = {};
while (payout_A != 0) {
auto state = get_account_state(client, alice.address);
if (!now) {
now = state.sync_utime;
}
CHECK(now.value() + 60 > state.sync_utime);
if (state.balance > 0) {
ASSERT_EQ(payout_A, state.balance);
LOG(INFO) << "Alice got: " << state.balance;
break;
}
client.receive(1);
}
LOG(INFO) << "Wait till Bob has its share";
now = {};
while (payout_B != 0) {
auto state = get_account_state(client, bob.address);
if (!now) {
now = state.sync_utime;
}
CHECK(now.value() + 60 > state.sync_utime);
if (state.balance > 0) {
ASSERT_EQ(payout_B, state.balance);
LOG(INFO) << "Bob got: " << state.balance;
break;
}
client.receive(1);
}
}
void test_dns(Client& client, const Wallet& giver_wallet) { void test_dns(Client& client, const Wallet& giver_wallet) {
auto A = create_empty_dns(client); auto A = create_empty_dns(client);
auto A_B = create_empty_dns(client); auto A_B = create_empty_dns(client);
@ -669,6 +834,7 @@ int main(int argc, char* argv[]) {
// give wallet with some test grams to run test // give wallet with some test grams to run test
auto giver_wallet = import_wallet_from_pkey(client, giver_key_str, giver_key_pwd); auto giver_wallet = import_wallet_from_pkey(client, giver_key_str, giver_key_pwd);
test_paychan(client, giver_wallet);
test_dns(client, giver_wallet); test_dns(client, giver_wallet);
test_back_and_forth_transfer(client, giver_wallet, false); test_back_and_forth_transfer(client, giver_wallet, false);
test_back_and_forth_transfer(client, giver_wallet, true); test_back_and_forth_transfer(client, giver_wallet, true);

View file

@ -101,6 +101,7 @@ class Client::Impl final {
scheduler_.run_in_context_external([] { td::actor::SchedulerContext::get()->stop(); }); scheduler_.run_in_context_external([] { td::actor::SchedulerContext::get()->stop(); });
LOG(ERROR) << "join"; LOG(ERROR) << "join";
scheduler_thread_.join(); scheduler_thread_.join();
LOG(ERROR) << "join - done";
} }
private: private:

View file

@ -121,6 +121,26 @@ td::Result<Config> Config::parse(std::string str) {
res.init_block_id = init_block_id; res.init_block_id = init_block_id;
} }
auto r_hardforks = td::get_json_object_field(validator, "hardforks", td::JsonValue::Type::Array, false);
if (r_hardforks.is_ok()) {
auto hardforks_obj = r_hardforks.move_as_ok();
auto &hardforks = hardforks_obj.get_array();
for (auto &fork : hardforks) {
if (fork.type() != td::JsonValue::Type::Object) {
return td::Status::Error("Invalid config (8)");
}
TRY_RESULT(fork_block, parse_block_id_ext(fork.get_object()));
res.hardforks.push_back(std::move(fork_block));
}
}
for (auto hardfork : res.hardforks) {
if (!res.init_block_id.is_valid() || hardfork.seqno() > res.init_block_id.seqno()) {
LOG(INFO) << "Replace init_block with hardfork: " << res.init_block_id.to_str() << " -> " << hardfork.to_str();
res.init_block_id = hardfork;
}
}
return res; return res;
} }
} // namespace tonlib } // namespace tonlib

View file

@ -29,7 +29,9 @@ struct Config {
}; };
ton::BlockIdExt zero_state_id; ton::BlockIdExt zero_state_id;
ton::BlockIdExt init_block_id; ton::BlockIdExt init_block_id;
std::vector<ton::BlockIdExt> hardforks;
std::vector<LiteClient> lite_clients; std::vector<LiteClient> lite_clients;
std::string name;
static td::Result<Config> parse(std::string str); static td::Result<Config> parse(std::string str);
}; };
} // namespace tonlib } // namespace tonlib

View file

@ -93,9 +93,10 @@ struct LastBlockState {
ton::BlockIdExt last_block_id; ton::BlockIdExt last_block_id;
td::int64 utime{0}; td::int64 utime{0};
ton::BlockIdExt init_block_id; ton::BlockIdExt init_block_id;
td::int32 vert_seqno{0};
static constexpr td::int32 magic = 0xa7f171a4; static constexpr td::int32 magic = 0xa7f171a4;
enum Version { None = 0, Magic, InitBlock, Next }; enum Version { None = 0, Magic, InitBlock, VertSeqno, Next };
static constexpr td::int32 version = Version::Next - 1; static constexpr td::int32 version = Version::Next - 1;
template <class StorerT> template <class StorerT>
@ -110,6 +111,7 @@ struct LastBlockState {
store(last_block_id, storer); store(last_block_id, storer);
store(utime, storer); store(utime, storer);
store(init_block_id, storer); store(init_block_id, storer);
store(vert_seqno, storer);
} }
template <class ParserT> template <class ParserT>
@ -130,6 +132,9 @@ struct LastBlockState {
if (version >= InitBlock) { if (version >= InitBlock) {
parse(init_block_id, parser); parse(init_block_id, parser);
} }
if (version >= VertSeqno) {
parse(vert_seqno, parser);
}
} }
}; };

View file

@ -36,6 +36,7 @@
#include "smc-envelope/HighloadWallet.h" #include "smc-envelope/HighloadWallet.h"
#include "smc-envelope/HighloadWalletV2.h" #include "smc-envelope/HighloadWalletV2.h"
#include "smc-envelope/TestGiver.h" #include "smc-envelope/TestGiver.h"
#include "smc-envelope/PaymentChannel.h"
#include "smc-envelope/SmartContractCode.h" #include "smc-envelope/SmartContractCode.h"
#include "auto/tl/tonlib_api.hpp" #include "auto/tl/tonlib_api.hpp"
@ -106,7 +107,8 @@ auto to_tonlib_api(const ton::BlockIdExt& blk) {
} }
tonlib_api::object_ptr<tonlib_api::options_configInfo> to_tonlib_api(const TonlibClient::FullConfig& full_config) { tonlib_api::object_ptr<tonlib_api::options_configInfo> to_tonlib_api(const TonlibClient::FullConfig& full_config) {
return tonlib_api::make_object<tonlib_api::options_configInfo>(full_config.wallet_id); return tonlib_api::make_object<tonlib_api::options_configInfo>(full_config.wallet_id,
full_config.rwallet_init_public_key);
} }
class TonlibQueryActor : public td::actor::Actor { class TonlibQueryActor : public td::actor::Actor {
@ -172,9 +174,69 @@ tonlib_api::object_ptr<tonlib_api::internal_transactionId> to_transaction_id(con
} }
std::string to_bytes(td::Ref<vm::Cell> cell) { std::string to_bytes(td::Ref<vm::Cell> cell) {
if (cell.is_null()) {
return "";
}
return vm::std_boc_serialize(cell, vm::BagOfCells::Mode::WithCRC32C).move_as_ok().as_slice().str(); return vm::std_boc_serialize(cell, vm::BagOfCells::Mode::WithCRC32C).move_as_ok().as_slice().str();
} }
td::Result<block::PublicKey> get_public_key(td::Slice public_key) {
TRY_RESULT_PREFIX(address, block::PublicKey::parse(public_key), TonlibError::InvalidPublicKey());
return address;
}
td::Result<block::StdAddress> get_account_address(td::Slice account_address) {
TRY_RESULT_PREFIX(address, block::StdAddress::parse(account_address), TonlibError::InvalidAccountAddress());
return address;
}
td::Result<block::PublicKey> public_key_from_bytes(td::Slice bytes) {
TRY_RESULT_PREFIX(key_bytes, block::PublicKey::from_bytes(bytes), TonlibError::Internal());
return key_bytes;
}
td::Result<ton::RestrictedWallet::InitData> to_init_data(const tonlib_api::rwallet_initialAccountState& rwallet_state) {
TRY_RESULT(init_key_bytes, get_public_key(rwallet_state.init_public_key_));
TRY_RESULT(key_bytes, get_public_key(rwallet_state.public_key_));
ton::RestrictedWallet::InitData init_data;
init_data.init_key = td::SecureString(init_key_bytes.key);
init_data.main_key = td::SecureString(key_bytes.key);
init_data.wallet_id = static_cast<td::uint32>(rwallet_state.wallet_id_);
return std::move(init_data);
}
td::Result<ton::pchan::Config> to_pchan_config(const tonlib_api::pchan_initialAccountState& pchan_state) {
ton::pchan::Config config;
if (!pchan_state.config_) {
return TonlibError::EmptyField("config");
}
TRY_RESULT_PREFIX(a_key, get_public_key(pchan_state.config_->alice_public_key_),
TonlibError::InvalidField("alice_public_key", ""));
config.a_key = td::SecureString(a_key.key);
TRY_RESULT_PREFIX(b_key, get_public_key(pchan_state.config_->bob_public_key_),
TonlibError::InvalidField("bob_public_key", ""));
config.b_key = td::SecureString(b_key.key);
if (!pchan_state.config_->alice_address_) {
return TonlibError::EmptyField("config.alice_address");
}
TRY_RESULT_PREFIX(a_addr, get_account_address(pchan_state.config_->alice_address_->account_address_),
TonlibError::InvalidField("alice_address", ""));
config.a_addr = std::move(a_addr);
if (!pchan_state.config_->bob_address_) {
return TonlibError::EmptyField("config.bob_address");
}
TRY_RESULT_PREFIX(b_addr, get_account_address(pchan_state.config_->bob_address_->account_address_),
TonlibError::InvalidField("bob_address", ""));
config.b_addr = std::move(b_addr);
config.channel_id = pchan_state.config_->channel_id_;
config.init_timeout = pchan_state.config_->init_timeout_;
config.close_timeout = pchan_state.config_->close_timeout_;
return std::move(config);
}
class AccountState { class AccountState {
public: public:
AccountState(block::StdAddress address, RawAccountState&& raw, td::uint32 wallet_id) AccountState(block::StdAddress address, RawAccountState&& raw, td::uint32 wallet_id)
@ -259,6 +321,55 @@ class AccountState {
TRY_RESULT(wallet_id, wallet.get_wallet_id()); TRY_RESULT(wallet_id, wallet.get_wallet_id());
return tonlib_api::make_object<tonlib_api::wallet_highload_v2_accountState>(static_cast<td::uint32>(wallet_id)); return tonlib_api::make_object<tonlib_api::wallet_highload_v2_accountState>(static_cast<td::uint32>(wallet_id));
} }
td::Result<tonlib_api::object_ptr<tonlib_api::rwallet_accountState>> to_rwallet_accountState() const {
if (wallet_type_ != RestrictedWallet) {
return TonlibError::AccountTypeUnexpected("RestrictedWallet");
}
auto wallet = ton::RestrictedWallet::create(get_smc_state());
TRY_RESULT(seqno, wallet->get_seqno());
TRY_RESULT(wallet_id, wallet->get_wallet_id());
TRY_RESULT(balance, wallet->get_balance(raw_.balance, raw_.info.gen_utime));
TRY_RESULT(config, wallet->get_config());
auto api_config = tonlib_api::make_object<tonlib_api::rwallet_config>();
api_config->start_at_ = config.start_at;
for (auto& limit : config.limits) {
api_config->limits_.push_back(tonlib_api::make_object<tonlib_api::rwallet_limit>(limit.first, limit.second));
}
return tonlib_api::make_object<tonlib_api::rwallet_accountState>(wallet_id, seqno, balance, std::move(api_config));
}
td::Result<tonlib_api::object_ptr<tonlib_api::pchan_accountState>> to_payment_channel_accountState() const {
if (wallet_type_ != PaymentChannel) {
return TonlibError::AccountTypeUnexpected("PaymentChannel");
}
auto pchan = ton::PaymentChannel::create(get_smc_state());
TRY_RESULT(info, pchan->get_info());
TRY_RESULT(a_key, public_key_from_bytes(info.config.a_key));
TRY_RESULT(b_key, public_key_from_bytes(info.config.b_key));
tonlib_api::object_ptr<tonlib_api::pchan_State> tl_state;
info.state.visit(td::overloaded(
[&](const ton::pchan::StateInit& state) {
tl_state = tonlib_api::make_object<tonlib_api::pchan_stateInit>(
state.signed_A, state.signed_B, state.min_A, state.min_B, state.A, state.B, state.expire_at);
},
[&](const ton::pchan::StateClose& state) {
tl_state = tonlib_api::make_object<tonlib_api::pchan_stateClose>(
state.signed_A, state.signed_B, state.promise_A, state.promise_B, state.A, state.B, state.expire_at);
},
[&](const ton::pchan::StatePayout& state) {
tl_state = tonlib_api::make_object<tonlib_api::pchan_statePayout>(state.A, state.B);
}));
using tonlib_api::make_object;
return tonlib_api::make_object<tonlib_api::pchan_accountState>(
tonlib_api::make_object<tonlib_api::pchan_config>(
a_key.serialize(true), make_object<tonlib_api::accountAddress>(info.config.a_addr.rserialize(true)),
b_key.serialize(true), make_object<tonlib_api::accountAddress>(info.config.b_addr.rserialize(true)),
info.config.init_timeout, info.config.close_timeout, info.config.channel_id),
std::move(tl_state), info.description);
}
td::Result<tonlib_api::object_ptr<tonlib_api::testGiver_accountState>> to_testGiver_accountState() const { td::Result<tonlib_api::object_ptr<tonlib_api::testGiver_accountState>> to_testGiver_accountState() const {
if (wallet_type_ != Giver) { if (wallet_type_ != Giver) {
@ -298,11 +409,14 @@ class AccountState {
return f(to_wallet_highload_v1_accountState()); return f(to_wallet_highload_v1_accountState());
case HighloadWalletV2: case HighloadWalletV2:
return f(to_wallet_highload_v2_accountState()); return f(to_wallet_highload_v2_accountState());
case RestrictedWallet:
return f(to_rwallet_accountState());
case ManualDns: case ManualDns:
return f(to_dns_accountState()); return f(to_dns_accountState());
default: case PaymentChannel:
UNREACHABLE(); return f(to_payment_channel_accountState());
} }
UNREACHABLE();
} }
td::Result<tonlib_api::object_ptr<tonlib_api::fullAccountState>> to_fullAccountState() const { td::Result<tonlib_api::object_ptr<tonlib_api::fullAccountState>> to_fullAccountState() const {
@ -321,7 +435,9 @@ class AccountState {
WalletV3, WalletV3,
HighloadWalletV1, HighloadWalletV1,
HighloadWalletV2, HighloadWalletV2,
ManualDns ManualDns,
PaymentChannel,
RestrictedWallet
}; };
WalletType get_wallet_type() const { WalletType get_wallet_type() const {
return wallet_type_; return wallet_type_;
@ -331,6 +447,7 @@ class AccountState {
case AccountState::Empty: case AccountState::Empty:
case AccountState::Unknown: case AccountState::Unknown:
case AccountState::ManualDns: case AccountState::ManualDns:
case AccountState::PaymentChannel:
return false; return false;
case AccountState::Giver: case AccountState::Giver:
case AccountState::SimpleWallet: case AccountState::SimpleWallet:
@ -338,6 +455,7 @@ class AccountState {
case AccountState::WalletV3: case AccountState::WalletV3:
case AccountState::HighloadWalletV1: case AccountState::HighloadWalletV1:
case AccountState::HighloadWalletV2: case AccountState::HighloadWalletV2:
case AccountState::RestrictedWallet:
return true; return true;
} }
UNREACHABLE(); UNREACHABLE();
@ -348,6 +466,7 @@ class AccountState {
case AccountState::Empty: case AccountState::Empty:
case AccountState::Unknown: case AccountState::Unknown:
case AccountState::ManualDns: case AccountState::ManualDns:
case AccountState::PaymentChannel:
return {}; return {};
case AccountState::Giver: case AccountState::Giver:
return td::make_unique<ton::TestGiver>(get_smc_state()); return td::make_unique<ton::TestGiver>(get_smc_state());
@ -361,6 +480,8 @@ class AccountState {
return td::make_unique<ton::HighloadWallet>(get_smc_state()); return td::make_unique<ton::HighloadWallet>(get_smc_state());
case AccountState::HighloadWalletV2: case AccountState::HighloadWalletV2:
return td::make_unique<ton::HighloadWalletV2>(get_smc_state()); return td::make_unique<ton::HighloadWalletV2>(get_smc_state());
case AccountState::RestrictedWallet:
return td::make_unique<ton::RestrictedWallet>(get_smc_state());
} }
UNREACHABLE(); UNREACHABLE();
return {}; return {};
@ -393,6 +514,50 @@ class AccountState {
return raw_; return raw_;
} }
WalletType guess_type_by_init_state(tonlib_api::InitialAccountState& initial_account_state) {
if (wallet_type_ != WalletType::Empty) {
return wallet_type_;
}
downcast_call(
initial_account_state,
td::overloaded(
[](auto& x) {},
[&](tonlib_api::rwallet_initialAccountState& rwallet) {
for (auto revision : ton::SmartContractCode::get_revisions(ton::SmartContractCode::RestrictedWallet)) {
auto r_init_data = to_init_data(rwallet);
if (r_init_data.is_error()) {
continue;
}
auto wallet = ton::RestrictedWallet::create(r_init_data.move_as_ok(), revision);
if (!(wallet->get_address() == address_)) {
continue;
}
wallet_type_ = WalletType::RestrictedWallet;
wallet_revision_ = revision;
set_new_state(wallet->get_state());
break;
}
},
[&](tonlib_api::pchan_initialAccountState& pchan) {
for (auto revision : ton::SmartContractCode::get_revisions(ton::SmartContractCode::PaymentChannel)) {
auto r_conf = to_pchan_config(pchan);
if (r_conf.is_error()) {
continue;
}
auto conf = r_conf.move_as_ok();
auto wallet = ton::PaymentChannel::create(conf, -1);
if (!(wallet->get_address() == address_)) {
continue;
}
wallet_type_ = WalletType::PaymentChannel;
wallet_revision_ = revision;
set_new_state(wallet->get_state());
break;
}
}));
return wallet_type_;
}
WalletType guess_type_by_public_key(td::Ed25519::PublicKey& key) { WalletType guess_type_by_public_key(td::Ed25519::PublicKey& key) {
if (wallet_type_ != WalletType::Empty) { if (wallet_type_ != WalletType::Empty) {
return wallet_type_; return wallet_type_;
@ -509,6 +674,18 @@ class AccountState {
wallet_revision_ = o_revision.value(); wallet_revision_ = o_revision.value();
return wallet_type_; return wallet_type_;
} }
o_revision = ton::PaymentChannel::guess_revision(code_hash);
if (o_revision) {
wallet_type_ = WalletType::PaymentChannel;
wallet_revision_ = o_revision.value();
return wallet_type_;
}
o_revision = ton::RestrictedWallet::guess_revision(code_hash);
if (o_revision) {
wallet_type_ = WalletType::RestrictedWallet;
wallet_revision_ = o_revision.value();
return wallet_type_;
}
if (code_hash == ton::TestGiver::get_init_code_hash()) { if (code_hash == ton::TestGiver::get_init_code_hash()) {
wallet_type_ = WalletType::Giver; wallet_type_ = WalletType::Giver;
@ -516,8 +693,6 @@ class AccountState {
wallet_type_ = WalletType::SimpleWallet; wallet_type_ = WalletType::SimpleWallet;
} else if (code_hash == ton::Wallet::get_init_code_hash()) { } else if (code_hash == ton::Wallet::get_init_code_hash()) {
wallet_type_ = WalletType::Wallet; wallet_type_ = WalletType::Wallet;
} else if (code_hash == ton::HighloadWallet::get_init_code_hash()) {
wallet_type_ = WalletType::HighloadWalletV1;
} else { } else {
LOG(WARNING) << "Unknown code hash: " << td::base64_encode(code_hash.as_slice()); LOG(WARNING) << "Unknown code hash: " << td::base64_encode(code_hash.as_slice());
wallet_type_ = WalletType::Unknown; wallet_type_ = WalletType::Unknown;
@ -545,6 +720,12 @@ class Query {
td::Ref<vm::Cell> get_message() const { td::Ref<vm::Cell> get_message() const {
return raw_.message; return raw_.message;
} }
td::Ref<vm::Cell> get_message_body() const {
return raw_.message_body;
}
td::Ref<vm::Cell> get_init_state() const {
return raw_.new_state;
}
vm::CellHash get_body_hash() const { vm::CellHash get_body_hash() const {
return raw_.message_body->get_hash(); return raw_.message_body->get_hash();
@ -722,11 +903,8 @@ class Query {
raw_.message_body, ton::SmartContract::Args().set_limits(gas_limits).set_ignore_chksig(ignore_chksig)); raw_.message_body, ton::SmartContract::Args().set_limits(gas_limits).set_ignore_chksig(ignore_chksig));
td::int64 fwd_fee = 0; td::int64 fwd_fee = 0;
if (res.success) { if (res.success) {
//std::cerr << "new smart contract data: "; LOG(DEBUG) << "output actions:\n"
//load_cell_slice(res.new_state.data).print_rec(std::cerr); << block::gen::OutList{output_actions_count(res.actions)}.as_string_ref(res.actions);
//std::cerr << "output actions: ";
//int out_act_num = output_actions_count(res.actions);
//block::gen::OutList{out_act_num}.print_ref(std::cerr, res.actions);
TRY_RESULT_ASSIGN(fwd_fee, calc_fwd_fees(res.actions, msg_prices, is_masterchain)); TRY_RESULT_ASSIGN(fwd_fee, calc_fwd_fees(res.actions, msg_prices, is_masterchain));
} }
@ -1402,11 +1580,6 @@ tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(const
return tonlib_api::make_object<tonlib_api::ok>(); return tonlib_api::make_object<tonlib_api::ok>();
} }
td::Result<block::PublicKey> get_public_key(td::Slice public_key) {
TRY_RESULT_PREFIX(address, block::PublicKey::parse(public_key), TonlibError::InvalidPublicKey());
return address;
}
td::Result<block::StdAddress> get_account_address(const tonlib_api::raw_initialAccountState& raw_state, td::Result<block::StdAddress> get_account_address(const tonlib_api::raw_initialAccountState& raw_state,
td::int32 revision) { td::int32 revision) {
TRY_RESULT_PREFIX(code, vm::std_boc_deserialize(raw_state.code_), TonlibError::InvalidBagOfCells("raw_state.code")); TRY_RESULT_PREFIX(code, vm::std_boc_deserialize(raw_state.code_), TonlibError::InvalidBagOfCells("raw_state.code"));
@ -1467,9 +1640,15 @@ td::Result<block::StdAddress> get_account_address(const tonlib_api::dns_initialA
return ton::ManualDns::create(key, static_cast<td::uint32>(dns_state.wallet_id_), revision)->get_address(); return ton::ManualDns::create(key, static_cast<td::uint32>(dns_state.wallet_id_), revision)->get_address();
} }
td::Result<block::StdAddress> get_account_address(td::Slice account_address) { td::Result<block::StdAddress> get_account_address(const tonlib_api::pchan_initialAccountState& pchan_state,
TRY_RESULT_PREFIX(address, block::StdAddress::parse(account_address), TonlibError::InvalidAccountAddress()); td::int32 revision) {
return address; TRY_RESULT(config, to_pchan_config(pchan_state));
return ton::PaymentChannel::create(config, revision)->get_address();
}
td::Result<block::StdAddress> get_account_address(const tonlib_api::rwallet_initialAccountState& rwallet_state,
td::int32 revision) {
TRY_RESULT(init_data, to_init_data(rwallet_state));
return ton::RestrictedWallet::create(init_data, revision)->get_address();
} }
td::Result<td::Bits256> get_adnl_address(td::Slice adnl_address) { td::Result<td::Bits256> get_adnl_address(td::Slice adnl_address) {
@ -1493,6 +1672,8 @@ static td::optional<ton::SmartContractCode::Type> get_wallet_type(tonlib_api::In
[](const tonlib_api::wallet_highload_v2_initialAccountState&) { [](const tonlib_api::wallet_highload_v2_initialAccountState&) {
return ton::SmartContractCode::HighloadWalletV2; return ton::SmartContractCode::HighloadWalletV2;
}, },
[](const tonlib_api::rwallet_initialAccountState&) { return ton::SmartContractCode::RestrictedWallet; },
[](const tonlib_api::pchan_initialAccountState&) { return ton::SmartContractCode::PaymentChannel; },
[](const tonlib_api::dns_initialAccountState&) { return ton::SmartContractCode::ManualDns; })); [](const tonlib_api::dns_initialAccountState&) { return ton::SmartContractCode::ManualDns; }));
} }
@ -1555,7 +1736,18 @@ td::Status TonlibClient::do_request(const tonlib_api::guessAccountRevision& requ
td::Promise<std::vector<int>> promise_; td::Promise<std::vector<int>> promise_;
size_t left_{0}; size_t left_{0};
std::vector<int> res; struct Item {
bool is_inited;
td::int64 balance;
int revision;
auto key() const {
return std::make_tuple(is_inited, balance, revision);
}
bool operator<(const Item& other) const {
return key() > other.key();
}
};
std::vector<Item> res;
void start_up() { void start_up() {
left_ += addresses_.size(); left_ += addresses_.size();
@ -1565,12 +1757,24 @@ td::Status TonlibClient::do_request(const tonlib_api::guessAccountRevision& requ
} }
} }
void on_account_state(int revision, td::Result<td::unique_ptr<AccountState>> r_state) { void on_account_state(int revision, td::Result<td::unique_ptr<AccountState>> r_state) {
if (r_state.is_ok() && r_state.ok()->get_wallet_type() != AccountState::WalletType::Empty) { SCOPE_EXIT {
res.push_back(revision); on_account_state_finish();
};
if (!r_state.is_ok()) {
return;
} }
auto state = r_state.move_as_ok();
if (state->get_balance() < 0) {
return;
}
res.push_back({state->get_wallet_type() != AccountState::WalletType::Empty, state->get_balance(), revision});
}
void on_account_state_finish() {
left_--; left_--;
if (left_ == 0) { if (left_ == 0) {
promise_.set_value(std::move(res)); std::sort(res.begin(), res.end());
promise_.set_value(td::transform(std::move(res), [](auto x) { return x.revision; }));
stop(); stop();
} }
} }
@ -1653,6 +1857,7 @@ class MasterConfig {
public: public:
void add_config(std::string name, std::string json) { void add_config(std::string name, std::string json) {
auto config = std::make_shared<Config>(Config::parse(json).move_as_ok()); auto config = std::make_shared<Config>(Config::parse(json).move_as_ok());
config->name = name;
if (!name.empty()) { if (!name.empty()) {
by_name_[name] = config; by_name_[name] = config;
} }
@ -1701,14 +1906,6 @@ const MasterConfig& get_default_master_config() {
})abc"); })abc");
res.add_config("testnet2", R"abc({ res.add_config("testnet2", R"abc({
"liteservers": [ "liteservers": [
{
"ip": 1137658550,
"port": 4924,
"id": {
"@type": "pub.ed25519",
"key": "peJTw/arlRfssgTuf9BMypJzqOi7SXEqSPSWiEw2U1M="
}
}
], ],
"validator": { "validator": {
"@type": "validator.config.global", "@type": "validator.config.global",
@ -1721,6 +1918,20 @@ const MasterConfig& get_default_master_config() {
}, },
"init_block": {"workchain":-1,"shard":-9223372036854775808,"seqno":2908451,"root_hash":"5+7X1QHVUBFLFMwa/yd/2fGzt2KeQtwr+o6UUFOQ7Qc=","file_hash":"gmiUgrtAbvEJZYDEkcbeNOhGPS3g+qCepSOEBFLZFzk="} "init_block": {"workchain":-1,"shard":-9223372036854775808,"seqno":2908451,"root_hash":"5+7X1QHVUBFLFMwa/yd/2fGzt2KeQtwr+o6UUFOQ7Qc=","file_hash":"gmiUgrtAbvEJZYDEkcbeNOhGPS3g+qCepSOEBFLZFzk="}
} }
})abc");
res.add_config("mainnet", R"abc({
"liteservers": [
],
"validator": {
"@type": "validator.config.global",
"zero_state": {
"workchain": -1,
"shard": -9223372036854775808,
"seqno": 0,
"root_hash": "KbTmuSarbve4ce9SET3BRyDnlPZr4GMdWswt1nDQAl4=",
"file_hash": "yRCjDRa-ChWiAGf5n3b25U17aqKzVL13C2Tzz8sqtJA="
}
}
})abc"); })abc");
return res; return res;
}(); }();
@ -1740,7 +1951,6 @@ td::Result<TonlibClient::FullConfig> TonlibClient::validate_config(tonlib_api::o
if (new_config.lite_clients.empty() && !config->use_callbacks_for_network_) { if (new_config.lite_clients.empty() && !config->use_callbacks_for_network_) {
return TonlibError::InvalidConfig("no lite clients"); return TonlibError::InvalidConfig("no lite clients");
} }
td::optional<Config> o_master_config; td::optional<Config> o_master_config;
std::string last_state_key; std::string last_state_key;
if (config->blockchain_name_.empty()) { if (config->blockchain_name_.empty()) {
@ -1755,6 +1965,12 @@ td::Result<TonlibClient::FullConfig> TonlibClient::validate_config(tonlib_api::o
return TonlibError::InvalidConfig("zero_state differs from embedded zero_state"); return TonlibError::InvalidConfig("zero_state differs from embedded zero_state");
} }
if (o_master_config && o_master_config.value().hardforks != new_config.hardforks) {
return TonlibError::InvalidConfig("hardforks differs from embedded hardforks");
}
int vert_seqno = static_cast<int>(new_config.hardforks.size());
LastBlockState state; LastBlockState state;
td::Result<LastBlockState> r_state; td::Result<LastBlockState> r_state;
if (!config->ignore_cache_) { if (!config->ignore_cache_) {
@ -1773,6 +1989,25 @@ td::Result<TonlibClient::FullConfig> TonlibClient::validate_config(tonlib_api::o
LOG(ERROR) << state.zero_state_id.to_str() << " " << zero_state.to_str(); LOG(ERROR) << state.zero_state_id.to_str() << " " << zero_state.to_str();
return TonlibError::InvalidConfig("zero_state differs from cached zero_state"); return TonlibError::InvalidConfig("zero_state differs from cached zero_state");
} }
if (state.vert_seqno > vert_seqno) {
LOG(ERROR) << "Stored vert_seqno is bigger than one in config: " << state.vert_seqno << " vs " << vert_seqno;
return TonlibError::InvalidConfig("vert_seqno in cached state is bigger");
}
if (state.vert_seqno < vert_seqno) {
state.zero_state_id = zero_state;
state.last_block_id = new_config.zero_state_id;
state.last_key_block_id = new_config.zero_state_id;
state.init_block_id = ton::BlockIdExt{};
LOG(WARNING) << "Drop cached state - vert_seqno is smaller than in config";
}
}
state.vert_seqno = vert_seqno;
//TODO: this could be useful to override master config
if (false && new_config.init_block_id.is_valid() &&
state.last_key_block_id.id.seqno < new_config.init_block_id.id.seqno) {
state.last_key_block_id = new_config.init_block_id;
LOG(INFO) << "Use init block from USER config: " << new_config.init_block_id.to_str();
} }
if (o_master_config) { if (o_master_config) {
@ -1782,12 +2017,23 @@ td::Result<TonlibClient::FullConfig> TonlibClient::validate_config(tonlib_api::o
state.last_key_block_id = master_config.init_block_id; state.last_key_block_id = master_config.init_block_id;
LOG(INFO) << "Use init block from MASTER config: " << master_config.init_block_id.to_str(); LOG(INFO) << "Use init block from MASTER config: " << master_config.init_block_id.to_str();
} }
if (!master_config.name.empty()) {
if (new_config.name != master_config.name) {
LOG(INFO) << "Use blockchain name from MASTER config: '" << master_config.name << "' (was '" << new_config.name
<< "')";
new_config.name = master_config.name;
}
}
} }
FullConfig res; FullConfig res;
res.config = std::move(new_config); res.config = std::move(new_config);
res.use_callbacks_for_network = config->use_callbacks_for_network_; res.use_callbacks_for_network = config->use_callbacks_for_network_;
res.wallet_id = td::as<td::uint32>(res.config.zero_state_id.root_hash.as_slice().data()); res.wallet_id = td::as<td::uint32>(res.config.zero_state_id.root_hash.as_slice().data());
if (res.config.name.empty()) { // TODO == "mainnet"
res.wallet_id = 0x4BA92D89;
}
res.rwallet_init_public_key = "Puasxr0QfFZZnYISRphVse7XHKfW7pZU5SJarVHXvQ+rpzkD";
res.last_state_key = std::move(last_state_key); res.last_state_key = std::move(last_state_key);
res.last_state = std::move(state); res.last_state = std::move(state);
@ -1906,7 +2152,7 @@ struct ToRawTransactions {
} }
} }
if (!data) { if (!data) {
data = tonlib_api::make_object<tonlib_api::msg_dataRaw>(to_bytes(std::move(body_cell))); data = tonlib_api::make_object<tonlib_api::msg_dataRaw>(to_bytes(std::move(body_cell)), "");
} }
return data; return data;
}; };
@ -2114,7 +2360,7 @@ td::Status TonlibClient::do_request(tonlib_api::raw_getTransactions& request,
td::optional<td::Ed25519::PrivateKey> private_key; td::optional<td::Ed25519::PrivateKey> private_key;
if (request.private_key_) { if (request.private_key_) {
TRY_RESULT(input_key, from_tonlib(*request.private_key_)); TRY_RESULT(input_key, from_tonlib(*request.private_key_));
//NB: options<Status> has lot of problems. We use emplace to migitate them //NB: optional<Status> has lot of problems. We use emplace to migitate them
td::optional<td::Status> o_status; td::optional<td::Status> o_status;
//NB: rely on (and assert) that GetPrivateKey is a synchonous request //NB: rely on (and assert) that GetPrivateKey is a synchonous request
make_request(int_api::GetPrivateKey{std::move(input_key)}, [&](auto&& r_key) { make_request(int_api::GetPrivateKey{std::move(input_key)}, [&](auto&& r_key) {
@ -2217,6 +2463,7 @@ class GenericCreateSendGrams : public TonlibQueryActor {
std::string message; std::string message;
td::Ref<vm::Cell> body; td::Ref<vm::Cell> body;
td::Ref<vm::Cell> init_state;
td::optional<td::Ed25519::PublicKey> o_public_key; td::optional<td::Ed25519::PublicKey> o_public_key;
}; };
@ -2227,6 +2474,10 @@ class GenericCreateSendGrams : public TonlibQueryActor {
// Should be splitted eventually // Should be splitted eventually
std::vector<ton::ManualDns::Action> dns_actions_; std::vector<ton::ManualDns::Action> dns_actions_;
bool pchan_action_{false};
bool rwallet_action_{false};
void check(td::Status status) { void check(td::Status status) {
if (status.is_error()) { if (status.is_error()) {
promise_.set_error(std::move(status)); promise_.set_error(std::move(status));
@ -2257,11 +2508,13 @@ class GenericCreateSendGrams : public TonlibQueryActor {
auto key = td::Ed25519::PublicKey(td::SecureString(public_key.key)); auto key = td::Ed25519::PublicKey(td::SecureString(public_key.key));
res.o_public_key = std::move(key); res.o_public_key = std::move(key);
} }
auto status = auto status = downcast_call2<td::Status>(
downcast_call2<td::Status>(*message.data_, td::overloaded( *message.data_, td::overloaded(
[&](tonlib_api::msg_dataRaw& text) { [&](tonlib_api::msg_dataRaw& text) {
TRY_RESULT(body, vm::std_boc_deserialize(text.body_)); TRY_RESULT(body, vm::std_boc_deserialize(text.body_));
TRY_RESULT(init_state, vm::std_boc_deserialize(text.init_state_, true));
res.body = std::move(body); res.body = std::move(body);
res.init_state = std::move(init_state);
return td::Status::OK(); return td::Status::OK();
}, },
[&](tonlib_api::msg_dataText& text) { [&](tonlib_api::msg_dataText& text) {
@ -2332,6 +2585,14 @@ class GenericCreateSendGrams : public TonlibQueryActor {
} }
return td::Status::OK(); return td::Status::OK();
}, },
[&](tonlib_api::actionPchan& cell) {
pchan_action_ = true;
return td::Status::OK();
},
[&](tonlib_api::actionRwallet& cell) {
rwallet_action_ = true;
return td::Status::OK();
},
[&](tonlib_api::actionDns& cell) { [&](tonlib_api::actionDns& cell) {
for (auto& from_action : cell.actions_) { for (auto& from_action : cell.actions_) {
if (!from_action) { if (!from_action) {
@ -2397,6 +2658,9 @@ class GenericCreateSendGrams : public TonlibQueryActor {
td::Status do_on_source_state(td::Result<td::unique_ptr<AccountState>> r_state) { td::Status do_on_source_state(td::Result<td::unique_ptr<AccountState>> r_state) {
TRY_RESULT(state, std::move(r_state)); TRY_RESULT(state, std::move(r_state));
source_ = std::move(state); source_ = std::move(state);
if (source_->get_wallet_type() == AccountState::Empty && query_.initial_account_state_) {
source_->guess_type_by_init_state(*query_.initial_account_state_);
}
if (source_->get_wallet_type() == AccountState::Empty && public_key_) { if (source_->get_wallet_type() == AccountState::Empty && public_key_) {
source_->guess_type_by_public_key(public_key_.value()); source_->guess_type_by_public_key(public_key_.value());
} }
@ -2459,6 +2723,165 @@ class GenericCreateSendGrams : public TonlibQueryActor {
return td::Status::OK(); return td::Status::OK();
} }
td::Status do_pchan_loop(td::Ref<ton::PaymentChannel> pchan, tonlib_api::actionPchan& action) {
if (!action.action_) {
return TonlibError::EmptyField("action");
}
Query::Raw raw;
auto valid_until = source_->get_sync_time();
valid_until += query_.timeout_ == 0 ? 60 : query_.timeout_;
raw.valid_until = valid_until;
TRY_RESULT(info, pchan->get_info());
bool is_alice = false;
bool is_bob = false;
if (info.config.a_key == private_key_.value().get_public_key().move_as_ok().as_octet_string()) {
LOG(ERROR) << "Alice key";
is_alice = true;
} else if (info.config.b_key == private_key_.value().get_public_key().move_as_ok().as_octet_string()) {
LOG(ERROR) << "Bob key";
is_bob = true;
}
if (!is_alice && !is_bob) {
return TonlibError::InvalidField("private_key", "invalid for this smartcontract");
}
auto status = downcast_call2<td::Status>(
*action.action_,
td::overloaded(
[&](tonlib_api::pchan_actionTimeout& timeout) {
auto builder = ton::pchan::MsgTimeoutBuilder();
if (is_alice) {
std::move(builder).with_a_key(&private_key_.value());
}
if (is_bob) {
std::move(builder).with_b_key(&private_key_.value());
}
raw.message_body = std::move(builder).finalize();
return td::Status::OK();
},
[&](tonlib_api::pchan_actionInit& init) {
auto builder = ton::pchan::MsgInitBuilder()
.inc_A(init.inc_A_)
.inc_B(init.inc_B_)
.min_A(init.min_A_)
.min_B(init.min_B_)
.channel_id(info.config.channel_id);
if (is_alice) {
std::move(builder).with_a_key(&private_key_.value());
}
if (is_bob) {
std::move(builder).with_b_key(&private_key_.value());
}
raw.message_body = std::move(builder).finalize();
return td::Status::OK();
},
[&](tonlib_api::pchan_actionClose& close) {
if (!close.promise_) {
return TonlibError::EmptyField("promise");
}
ton::pchan::SignedPromiseBuilder sbuilder;
sbuilder.promise_A(close.promise_->promise_A_)
.promise_B(close.promise_->promise_B_)
.channel_id(close.promise_->channel_id_)
.signature(td::SecureString(close.promise_->signature_));
if (is_alice && !sbuilder.check_signature(close.promise_->signature_,
td::Ed25519::PublicKey(info.config.b_key.copy()))) {
return TonlibError::InvalidSignature();
}
if (is_bob && !sbuilder.check_signature(close.promise_->signature_,
td::Ed25519::PublicKey(info.config.a_key.copy()))) {
return TonlibError::InvalidSignature();
}
auto builder = ton::pchan::MsgCloseBuilder()
.extra_A(close.extra_A_)
.extra_B(close.extra_B_)
.signed_promise(sbuilder.finalize());
if (is_alice) {
std::move(builder).with_a_key(&private_key_.value());
}
if (is_bob) {
std::move(builder).with_b_key(&private_key_.value());
}
raw.message_body = std::move(builder).finalize();
return td::Status::OK();
}));
TRY_STATUS(std::move(status));
raw.new_state = source_->get_new_state();
raw.message = ton::GenericAccount::create_ext_message(source_->get_address(), raw.new_state, raw.message_body);
raw.source = std::move(source_);
promise_.set_value(td::make_unique<Query>(std::move(raw)));
stop();
return td::Status::OK();
}
td::Status do_pchan_loop() {
if (!private_key_) {
return TonlibError::EmptyField("private_key");
}
auto pchan = ton::PaymentChannel::create(source_->get_smc_state());
return downcast_call2<td::Status>(
*query_.action_, td::overloaded([&](tonlib_api::actionNoop& cell) { return td::Status::OK(); },
[&](auto& cell) { return td::Status::Error(); },
[&](tonlib_api::actionPchan& cell) { return do_pchan_loop(pchan, cell); }));
}
td::Status do_rwallet_action(td::Ref<ton::RestrictedWallet> rwallet, tonlib_api::actionRwallet& action) {
if (!action.action_) {
return TonlibError::EmptyField("action");
}
auto& init = *action.action_;
if (!init.config_) {
return TonlibError::EmptyField("config");
}
TRY_RESULT_PREFIX(start_at, td::narrow_cast_safe<td::uint32>(init.config_->start_at_),
TonlibError::InvalidField("start_at", "not a uint32"));
ton::RestrictedWallet::Config config;
config.start_at = start_at;
for (auto& limit : init.config_->limits_) {
if (!limit) {
return TonlibError::EmptyField("limits");
}
TRY_RESULT_PREFIX(seconds, td::narrow_cast_safe<td::int32>(limit->seconds_),
TonlibError::InvalidField("seconds", "not a int32"));
TRY_RESULT_PREFIX(value, td::narrow_cast_safe<td::uint64>(limit->value_),
TonlibError::InvalidField("value", "not a uint64"));
config.limits.emplace_back(seconds, value);
}
Query::Raw raw;
auto valid_until = source_->get_sync_time();
valid_until += query_.timeout_ == 0 ? 60 : query_.timeout_;
raw.valid_until = valid_until;
TRY_RESULT_PREFIX(message_body, rwallet->get_init_message(private_key_.value(), valid_until, config),
TonlibError::Internal("Invalid rwalet init query"));
raw.message_body = std::move(message_body);
raw.new_state = source_->get_new_state();
raw.message = ton::GenericAccount::create_ext_message(source_->get_address(), raw.new_state, raw.message_body);
raw.source = std::move(source_);
raw.destinations = std::move(destinations_);
promise_.set_value(td::make_unique<Query>(std::move(raw)));
stop();
return td::Status::OK();
}
td::Status do_rwallet_action() {
if (!private_key_) {
return TonlibError::EmptyField("private_key");
}
auto rwallet = ton::RestrictedWallet::create(source_->get_smc_state());
LOG(ERROR) << rwallet->get_address().rserialize(true);
return downcast_call2<td::Status>(
*query_.action_,
td::overloaded([&](auto& cell) { return td::Status::Error("UNREACHABLE"); },
[&](tonlib_api::actionRwallet& cell) { return do_rwallet_action(rwallet, cell); }));
}
td::Status do_loop() { td::Status do_loop() {
if (!source_ || destinations_left_ != 0) { if (!source_ || destinations_left_ != 0) {
return td::Status::OK(); return td::Status::OK();
@ -2470,6 +2893,12 @@ class GenericCreateSendGrams : public TonlibQueryActor {
if (source_->get_wallet_type() == AccountState::ManualDns) { if (source_->get_wallet_type() == AccountState::ManualDns) {
return do_dns_loop(); return do_dns_loop();
} }
if (source_->get_wallet_type() == AccountState::PaymentChannel) {
return do_pchan_loop();
}
if (rwallet_action_) {
return do_rwallet_action();
}
switch (source_->get_wallet_type()) { switch (source_->get_wallet_type()) {
case AccountState::Empty: case AccountState::Empty:
@ -2507,6 +2936,7 @@ class GenericCreateSendGrams : public TonlibQueryActor {
} }
if (action.body.not_null()) { if (action.body.not_null()) {
gift.body = action.body; gift.body = action.body;
gift.init_state = action.init_state;
} else if (action.should_encrypt) { } else if (action.should_encrypt) {
LOG(ERROR) << "TRY ENCRYPT"; LOG(ERROR) << "TRY ENCRYPT";
if (!private_key_) { if (!private_key_) {
@ -2581,7 +3011,7 @@ class GenericCreateSendGrams : public TonlibQueryActor {
return with_wallet(*source_->get_wallet()); return with_wallet(*source_->get_wallet());
} }
}; }; // namespace tonlib
td::int64 TonlibClient::register_query(td::unique_ptr<Query> query) { td::int64 TonlibClient::register_query(td::unique_ptr<Query> query) {
auto query_id = ++next_query_id_; auto query_id = ++next_query_id_;
@ -2594,8 +3024,9 @@ td::Result<tonlib_api::object_ptr<tonlib_api::query_info>> TonlibClient::get_que
if (it == queries_.end()) { if (it == queries_.end()) {
return TonlibError::InvalidQueryId(); return TonlibError::InvalidQueryId();
} }
return tonlib_api::make_object<tonlib_api::query_info>(id, it->second->get_valid_until(), return tonlib_api::make_object<tonlib_api::query_info>(
it->second->get_body_hash().as_slice().str()); id, it->second->get_valid_until(), it->second->get_body_hash().as_slice().str(),
to_bytes(it->second->get_message_body()), to_bytes(it->second->get_init_state()));
} }
void TonlibClient::finish_create_query(td::Result<td::unique_ptr<Query>> r_query, void TonlibClient::finish_create_query(td::Result<td::unique_ptr<Query>> r_query,
@ -2938,6 +3369,8 @@ td::Status TonlibClient::do_request(const tonlib_api::smc_runGetMethod& request,
stack.write().push(std::move(e)); stack.write().push(std::move(e));
} }
args.set_stack(std::move(stack)); args.set_stack(std::move(stack));
args.set_balance(it->second->get_balance());
args.set_now(it->second->get_sync_time());
auto res = smc->run_get_method(std::move(args)); auto res = smc->run_get_method(std::move(args));
// smc.runResult gas_used:int53 stack:vector<tvm.StackEntry> exit_code:int32 = smc.RunResult; // smc.runResult gas_used:int53 stack:vector<tvm.StackEntry> exit_code:int32 = smc.RunResult;
@ -3057,6 +3490,76 @@ td::Status TonlibClient::do_request(const tonlib_api::dns_resolve& request,
return td::Status::OK(); return td::Status::OK();
} }
td::Status TonlibClient::do_request(tonlib_api::pchan_signPromise& request,
td::Promise<object_ptr<tonlib_api::pchan_promise>>&& promise) {
if (!request.promise_) {
return TonlibError::EmptyField("promise");
}
if (!request.input_key_) {
return TonlibError::EmptyField("input_key");
}
TRY_RESULT(input_key, from_tonlib(*request.input_key_));
make_request(int_api::GetPrivateKey{std::move(input_key)},
promise.wrap([promise = std::move(request.promise_)](auto key) mutable {
auto private_key = td::Ed25519::PrivateKey(std::move(key.private_key));
promise->signature_ = ton::pchan::SignedPromiseBuilder()
.promise_A(promise->promise_A_)
.promise_B(promise->promise_B_)
.channel_id(promise->channel_id_)
.with_key(&private_key)
.calc_signature()
.as_slice()
.str();
return std::move(promise);
}));
return td::Status::OK();
}
td::Status TonlibClient::do_request(tonlib_api::pchan_validatePromise& request,
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
if (!request.promise_) {
return TonlibError::EmptyField("promise");
}
TRY_RESULT(key_bytes, get_public_key(request.public_key_));
auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key));
bool is_ok = ton::pchan::SignedPromiseBuilder()
.promise_A(request.promise_->promise_A_)
.promise_B(request.promise_->promise_B_)
.channel_id(request.promise_->channel_id_)
.check_signature(request.promise_->signature_, key);
if (!is_ok) {
return TonlibError::InvalidSignature();
}
promise.set_value(tonlib_api::make_object<tonlib_api::ok>());
return td::Status::OK();
}
td::Status TonlibClient::do_request(tonlib_api::pchan_packPromise& request,
td::Promise<object_ptr<tonlib_api::data>>&& promise) {
if (!request.promise_) {
return TonlibError::EmptyField("promise");
}
promise.set_value(tonlib_api::make_object<tonlib_api::data>(
td::SecureString(to_bytes(ton::pchan::SignedPromiseBuilder()
.promise_A(request.promise_->promise_A_)
.promise_B(request.promise_->promise_B_)
.channel_id(request.promise_->channel_id_)
.signature(td::SecureString(request.promise_->signature_))
.finalize()))));
return td::Status::OK();
}
td::Status TonlibClient::do_request(tonlib_api::pchan_unpackPromise& request,
td::Promise<object_ptr<tonlib_api::pchan_promise>>&& promise) {
TRY_RESULT_PREFIX(body, vm::std_boc_deserialize(request.data_), TonlibError::InvalidBagOfCells("data"));
ton::pchan::SignedPromise spromise;
if (!spromise.unpack(body)) {
return TonlibError::InvalidField("data", "Can't unpack as a promise");
}
promise.set_value(tonlib_api::make_object<tonlib_api::pchan_promise>(
spromise.o_signature.value().as_slice().str(), spromise.promise.promise_A, spromise.promise.promise_B,
spromise.promise.channel_id));
return td::Status::OK();
}
td::Status TonlibClient::do_request(tonlib_api::sync& request, td::Status TonlibClient::do_request(tonlib_api::sync& request,
td::Promise<object_ptr<tonlib_api::ton_blockIdExt>>&& promise) { td::Promise<object_ptr<tonlib_api::ton_blockIdExt>>&& promise) {
// ton.blockIdExt workchain:int32 shard:int64 seqno:int32 root_hash:bytes file_hash:bytes = ton.BlockIdExt; // ton.blockIdExt workchain:int32 shard:int64 seqno:int32 root_hash:bytes file_hash:bytes = ton.BlockIdExt;
@ -3067,11 +3570,6 @@ td::Status TonlibClient::do_request(tonlib_api::sync& request,
return td::Status::OK(); return td::Status::OK();
} }
td::Result<block::PublicKey> public_key_from_bytes(td::Slice bytes) {
TRY_RESULT_PREFIX(key_bytes, block::PublicKey::from_bytes(bytes), TonlibError::Internal());
return key_bytes;
}
td::Status TonlibClient::do_request(const tonlib_api::createNewKey& request, td::Status TonlibClient::do_request(const tonlib_api::createNewKey& request,
td::Promise<object_ptr<tonlib_api::key>>&& promise) { td::Promise<object_ptr<tonlib_api::key>>&& promise) {
TRY_RESULT_PREFIX( TRY_RESULT_PREFIX(

View file

@ -74,6 +74,7 @@ class TonlibClient : public td::actor::Actor {
LastBlockState last_state; LastBlockState last_state;
std::string last_state_key; std::string last_state_key;
td::uint32 wallet_id; td::uint32 wallet_id;
std::string rwallet_init_public_key;
}; };
private: private:
@ -307,6 +308,15 @@ class TonlibClient : public td::actor::Actor {
td::Status do_request(const tonlib_api::dns_resolve& request, td::Status do_request(const tonlib_api::dns_resolve& request,
td::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise); td::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise);
td::Status do_request(tonlib_api::pchan_signPromise& request,
td::Promise<object_ptr<tonlib_api::pchan_promise>>&& promise);
td::Status do_request(tonlib_api::pchan_validatePromise& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
td::Status do_request(tonlib_api::pchan_packPromise& request, td::Promise<object_ptr<tonlib_api::data>>&& promise);
td::Status do_request(tonlib_api::pchan_unpackPromise& request,
td::Promise<object_ptr<tonlib_api::pchan_promise>>&& promise);
void do_dns_request(std::string name, td::int32 category, td::int32 ttl, td::optional<ton::BlockIdExt> block_id, void do_dns_request(std::string name, td::int32 category, td::int32 ttl, td::optional<ton::BlockIdExt> block_id,
block::StdAddress address, td::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise); block::StdAddress address, td::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise);
struct DnsFinishData { struct DnsFinishData {

View file

@ -32,7 +32,7 @@
// INVALID_ACCOUNT_ADDRESS // INVALID_ACCOUNT_ADDRESS
// INVALID_CONFIG // INVALID_CONFIG
// INVALID_PEM_KEY // INVALID_PEM_KEY
// INVALID_REVISION // INVALID_SIGNATURE
// MESSAGE_TOO_LONG // MESSAGE_TOO_LONG
// EMPTY_FIELD // EMPTY_FIELD
// INVALID_FIELD // INVALID_FIELD
@ -84,6 +84,9 @@ struct TonlibError {
static td::Status InvalidRevision() { static td::Status InvalidRevision() {
return td::Status::Error(400, "INVALID_REVISION"); return td::Status::Error(400, "INVALID_REVISION");
} }
static td::Status InvalidSignature() {
return td::Status::Error(400, "INVALID_SIGNATURE");
}
static td::Status NeedConfig() { static td::Status NeedConfig() {
return td::Status::Error(400, "NeedConfig"); return td::Status::Error(400, "NeedConfig");
} }

File diff suppressed because it is too large Load diff

View file

@ -1172,13 +1172,16 @@ td::Status ValidatorEngine::load_global_config() {
validator_options_ = ton::validator::ValidatorManagerOptions::create(zero_state, init_block); validator_options_ = ton::validator::ValidatorManagerOptions::create(zero_state, init_block);
validator_options_.write().set_shard_check_function( validator_options_.write().set_shard_check_function(
[](ton::ShardIdFull shard, ton::validator::ValidatorManagerOptions::ShardCheckMode mode) -> bool { [](ton::ShardIdFull shard, ton::CatchainSeqno cc_seqno,
ton::validator::ValidatorManagerOptions::ShardCheckMode mode) -> bool {
if (mode == ton::validator::ValidatorManagerOptions::ShardCheckMode::m_monitor) { if (mode == ton::validator::ValidatorManagerOptions::ShardCheckMode::m_monitor) {
return true; return true;
} }
CHECK(mode == ton::validator::ValidatorManagerOptions::ShardCheckMode::m_validate); CHECK(mode == ton::validator::ValidatorManagerOptions::ShardCheckMode::m_validate);
//return shard.is_masterchain();
return true; return true;
/*ton::ShardIdFull p{ton::basechainId, ((cc_seqno * 1ull % 4) << 62) + 1};
auto s = ton::shard_prefix(p, 2);
return shard.is_masterchain() || ton::shard_intersects(shard, s);*/
}); });
if (state_ttl_ != 0) { if (state_ttl_ != 0) {
validator_options_.write().set_state_ttl(state_ttl_); validator_options_.write().set_state_ttl(state_ttl_);
@ -1195,12 +1198,12 @@ td::Status ValidatorEngine::load_global_config() {
if (key_proof_ttl_ != 0) { if (key_proof_ttl_ != 0) {
validator_options_.write().set_key_proof_ttl(key_proof_ttl_); validator_options_.write().set_key_proof_ttl(key_proof_ttl_);
} }
if (db_depth_ <= 32) {
validator_options_.write().set_filedb_depth(db_depth_);
}
for (auto seq : unsafe_catchains_) { for (auto seq : unsafe_catchains_) {
validator_options_.write().add_unsafe_resync_catchain(seq); validator_options_.write().add_unsafe_resync_catchain(seq);
} }
if (truncate_seqno_ > 0) {
validator_options_.write().truncate_db(truncate_seqno_);
}
std::vector<ton::BlockIdExt> h; std::vector<ton::BlockIdExt> h;
for (auto &x : conf.validator_->hardforks_) { for (auto &x : conf.validator_->hardforks_) {
@ -3079,6 +3082,10 @@ std::atomic<bool> rotate_logs_flags{false};
void force_rotate_logs(int sig) { void force_rotate_logs(int sig) {
rotate_logs_flags.store(true); rotate_logs_flags.store(true);
} }
std::atomic<bool> need_scheduler_status_flag{false};
void need_scheduler_status(int sig) {
need_scheduler_status_flag.store(true);
}
void dump_memory_stats() { void dump_memory_stats() {
if (!is_memprof_on()) { if (!is_memprof_on()) {
@ -3164,15 +3171,6 @@ int main(int argc, char *argv[]) {
acts.push_back([&x, fname = fname.str()]() { td::actor::send_closure(x, &ValidatorEngine::set_fift_dir, fname); }); acts.push_back([&x, fname = fname.str()]() { td::actor::send_closure(x, &ValidatorEngine::set_fift_dir, fname); });
return td::Status::OK(); return td::Status::OK();
}); });
p.add_option('F', "filedb-depth",
"depth of autodirs for blocks, proofs, etc. Default value is 2. You need to clear the "
"database, if you need to change this option",
[&](td::Slice fname) {
acts.push_back([&x, fname = fname.str()]() {
td::actor::send_closure(x, &ValidatorEngine::set_db_depth, td::to_integer<td::uint32>(fname));
});
return td::Status::OK();
});
p.add_option('d', "daemonize", "set SIGHUP", [&]() { p.add_option('d', "daemonize", "set SIGHUP", [&]() {
#if TD_DARWIN || TD_LINUX #if TD_DARWIN || TD_LINUX
close(0); close(0);
@ -3215,6 +3213,12 @@ int main(int argc, char *argv[]) {
acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_sync_ttl, v); }); acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_sync_ttl, v); });
return td::Status::OK(); return td::Status::OK();
}); });
p.add_option('T', "truncate-db", "truncate db (with specified seqno as new top masterchain block seqno)",
[&](td::Slice fname) {
auto v = td::to_integer<ton::BlockSeqno>(fname);
acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_truncate_seqno, v); });
return td::Status::OK();
});
p.add_option('U', "unsafe-catchain-restore", "use SLOW and DANGEROUS catchain recover method", [&](td::Slice id) { p.add_option('U', "unsafe-catchain-restore", "use SLOW and DANGEROUS catchain recover method", [&](td::Slice id) {
TRY_RESULT(seq, td::to_integer_safe<ton::CatchainSeqno>(id)); TRY_RESULT(seq, td::to_integer_safe<ton::CatchainSeqno>(id));
acts.push_back([&x, seq]() { td::actor::send_closure(x, &ValidatorEngine::add_unsafe_catchain, seq); }); acts.push_back([&x, seq]() { td::actor::send_closure(x, &ValidatorEngine::add_unsafe_catchain, seq); });
@ -3242,7 +3246,9 @@ int main(int argc, char *argv[]) {
} }
td::set_runtime_signal_handler(1, need_stats).ensure(); td::set_runtime_signal_handler(1, need_stats).ensure();
td::set_runtime_signal_handler(2, need_scheduler_status).ensure();
td::actor::set_debug(true);
td::actor::Scheduler scheduler({threads}); td::actor::Scheduler scheduler({threads});
scheduler.run_in_context([&] { scheduler.run_in_context([&] {
@ -3258,6 +3264,10 @@ int main(int argc, char *argv[]) {
if (need_stats_flag.exchange(false)) { if (need_stats_flag.exchange(false)) {
dump_stats(); dump_stats();
} }
if (need_scheduler_status_flag.exchange(false)) {
LOG(ERROR) << "DUMPING SCHEDULER STATISTICS";
scheduler.get_debug().dump();
}
if (rotate_logs_flags.exchange(false)) { if (rotate_logs_flags.exchange(false)) {
if (td::log_interface) { if (td::log_interface) {
td::log_interface->rotate(); td::log_interface->rotate();

View file

@ -195,10 +195,10 @@ class ValidatorEngine : public td::actor::Actor {
td::Clocks::Duration sync_ttl_ = 0; td::Clocks::Duration sync_ttl_ = 0;
td::Clocks::Duration archive_ttl_ = 0; td::Clocks::Duration archive_ttl_ = 0;
td::Clocks::Duration key_proof_ttl_ = 0; td::Clocks::Duration key_proof_ttl_ = 0;
td::uint32 db_depth_ = 33;
bool read_config_ = false; bool read_config_ = false;
bool started_keyring_ = false; bool started_keyring_ = false;
bool started_ = false; bool started_ = false;
ton::BlockSeqno truncate_seqno_{0};
std::set<ton::CatchainSeqno> unsafe_catchains_; std::set<ton::CatchainSeqno> unsafe_catchains_;
@ -216,9 +216,6 @@ class ValidatorEngine : public td::actor::Actor {
fift_dir_ = str; fift_dir_ = str;
} }
void set_db_root(std::string db_root); void set_db_root(std::string db_root);
void set_db_depth(td::uint32 value) {
db_depth_ = value;
}
void set_state_ttl(td::Clocks::Duration t) { void set_state_ttl(td::Clocks::Duration t) {
state_ttl_ = t; state_ttl_ = t;
} }
@ -234,6 +231,9 @@ class ValidatorEngine : public td::actor::Actor {
void set_key_proof_ttl(td::Clocks::Duration t) { void set_key_proof_ttl(td::Clocks::Duration t) {
key_proof_ttl_ = t; key_proof_ttl_ = t;
} }
void set_truncate_seqno(ton::BlockSeqno seqno) {
truncate_seqno_ = seqno;
}
void add_ip(td::IPAddress addr) { void add_ip(td::IPAddress addr) {
addrs_.push_back(addr); addrs_.push_back(addr);
} }

View file

@ -134,7 +134,7 @@ std::vector<PublicKey> ValidatorSessionDescriptionImpl::export_full_nodes() cons
} }
double ValidatorSessionDescriptionImpl::get_delay(td::uint32 priority) const { double ValidatorSessionDescriptionImpl::get_delay(td::uint32 priority) const {
return priority * opts_.next_candidate_delay; return ((sources_.size() >= 5 ? 0 : 1) + priority) * opts_.next_candidate_delay;
} }
td::uint32 ValidatorSessionDescriptionImpl::get_vote_for_author(td::uint32 attempt_seqno) const { td::uint32 ValidatorSessionDescriptionImpl::get_vote_for_author(td::uint32 attempt_seqno) const {

View file

@ -110,6 +110,25 @@ set(DISK_VALIDATOR_SOURCE
${VALIDATOR_DB_SOURCE} ${VALIDATOR_DB_SOURCE}
) )
set(HARDFORK_VALIDATOR_SOURCE
apply-block.cpp
block-handle.cpp
shard-client.cpp
validator-full-id.cpp
validator-group.cpp
validator-options.cpp
downloaders/wait-block-data-disk.cpp
downloaders/wait-block-state.cpp
downloaders/wait-block-state-merge.cpp
downloaders/download-state.cpp
manager-init.cpp
manager-hardfork.cpp
${VALIDATOR_DB_SOURCE}
)
set(FULL_NODE_SOURCE set(FULL_NODE_SOURCE
full-node.h full-node.h
full-node.hpp full-node.hpp
@ -139,6 +158,7 @@ set(FULL_NODE_SOURCE
add_library(validator STATIC ${VALIDATOR_SOURCE}) add_library(validator STATIC ${VALIDATOR_SOURCE})
add_library(validator-disk STATIC ${DISK_VALIDATOR_SOURCE}) add_library(validator-disk STATIC ${DISK_VALIDATOR_SOURCE})
add_library(validator-hardfork STATIC ${HARDFORK_VALIDATOR_SOURCE})
add_library(full-node STATIC ${FULL_NODE_SOURCE}) add_library(full-node STATIC ${FULL_NODE_SOURCE})
target_include_directories(validator PUBLIC target_include_directories(validator PUBLIC
@ -155,6 +175,13 @@ target_include_directories(validator-disk PUBLIC
${OPENSSL_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR}
) )
target_include_directories(validator-hardfork PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/..
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/../crypto
${OPENSSL_INCLUDE_DIR}
)
target_include_directories(full-node PUBLIC target_include_directories(full-node PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/.. $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/..
@ -168,5 +195,8 @@ target_link_libraries(validator PRIVATE tdutils tdactor adnl rldp tl_api dht tdf
target_link_libraries(validator-disk PRIVATE tdutils tdactor adnl rldp tl_api dht tdfec target_link_libraries(validator-disk PRIVATE tdutils tdactor adnl rldp tl_api dht tdfec
overlay catchain validatorsession ton_crypto ton_block ton_db) overlay catchain validatorsession ton_crypto ton_block ton_db)
target_link_libraries(validator-hardfork PRIVATE tdutils tdactor adnl rldp tl_api dht tdfec
overlay catchain validatorsession ton_crypto ton_block ton_db)
target_link_libraries(full-node PRIVATE tdutils tdactor adnl rldp tl_api dht tdfec target_link_libraries(full-node PRIVATE tdutils tdactor adnl rldp tl_api dht tdfec
overlay catchain validatorsession ton_crypto ton_block ton_db) overlay catchain validatorsession ton_crypto ton_block ton_db)

View file

@ -42,8 +42,8 @@ td::BufferSlice BlockHandleImpl::serialize() const {
(flags & dbf_inited_masterchain_ref_block) ? masterchain_ref_seqno_ : 0); (flags & dbf_inited_masterchain_ref_block) ? masterchain_ref_seqno_ : 0);
} }
BlockHandleImpl::BlockHandleImpl(td::BufferSlice data) { BlockHandleImpl::BlockHandleImpl(td::Slice data) {
auto obj = fetch_tl_object<ton_api::db_block_info>(std::move(data), true).move_as_ok(); auto obj = fetch_tl_object<ton_api::db_block_info>(data, true).move_as_ok();
flags_ = obj->flags_ & ~(Flags::dbf_processed | Flags::dbf_moved_handle); flags_ = obj->flags_ & ~(Flags::dbf_processed | Flags::dbf_moved_handle);
id_ = create_block_id(obj->id_); id_ = create_block_id(obj->id_);
prev_[0] = (flags_ & dbf_inited_prev_left) ? create_block_id(obj->prev_left_) : BlockIdExt{}; prev_[0] = (flags_ & dbf_inited_prev_left) ? create_block_id(obj->prev_left_) : BlockIdExt{};

View file

@ -517,7 +517,7 @@ struct BlockHandleImpl : public BlockHandleInterface {
: id_(id), flags_(id_.is_masterchain() ? static_cast<td::uint32>(dbf_masterchain) : 0) { : id_(id), flags_(id_.is_masterchain() ? static_cast<td::uint32>(dbf_masterchain) : 0) {
get_thread_safe_counter().add(1); get_thread_safe_counter().add(1);
} }
BlockHandleImpl(td::BufferSlice data); BlockHandleImpl(td::Slice data);
~BlockHandleImpl() { ~BlockHandleImpl() {
LOG_CHECK(!need_flush()) << "flags=" << flags_; LOG_CHECK(!need_flush()) << "flags=" << flags_;
get_thread_safe_counter().add(-1); get_thread_safe_counter().add(-1);
@ -532,7 +532,7 @@ struct BlockHandleImpl : public BlockHandleInterface {
return std::make_shared<BlockHandleImpl>(id); return std::make_shared<BlockHandleImpl>(id);
} }
static BlockHandle create(td::BufferSlice data) { static BlockHandle create(td::Slice data) {
return std::make_shared<BlockHandleImpl>(std::move(data)); return std::make_shared<BlockHandleImpl>(std::move(data));
} }
}; };

View file

@ -77,10 +77,20 @@ void ArchiveManager::update_handle(BlockHandle handle, td::Promise<td::Unit> pro
FileDescription *f; FileDescription *f;
if (handle->handle_moved_to_archive()) { if (handle->handle_moved_to_archive()) {
CHECK(handle->inited_unix_time()); CHECK(handle->inited_unix_time());
if (!handle->need_flush()) {
promise.set_value(td::Unit());
return;
}
f = get_file_desc(handle->id().shard_full(), get_package_id(handle->masterchain_ref_block()), handle->id().seqno(), f = get_file_desc(handle->id().shard_full(), get_package_id(handle->masterchain_ref_block()), handle->id().seqno(),
handle->unix_time(), handle->logical_time(), true); handle->unix_time(), handle->logical_time(), true);
if (!f) {
handle->flushed_upto(handle->version());
promise.set_value(td::Unit());
return;
}
} else { } else {
f = get_file_desc(handle->id().shard_full(), get_temp_package_id(), 0, 0, 0, true); f = get_file_desc(handle->id().shard_full(), get_temp_package_id(), 0, 0, 0, true);
CHECK(f);
} }
td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::update_handle, std::move(handle), std::move(promise)); td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::update_handle, std::move(handle), std::move(promise));
} }
@ -540,7 +550,7 @@ void ArchiveManager::load_package(PackageId id) {
} }
} }
desc.file = td::actor::create_actor<ArchiveSlice>("slice", id.id, id.key, id.temp, db_root_); desc.file = td::actor::create_actor<ArchiveSlice>("slice", id.id, id.key, id.temp, false, db_root_);
get_file_map(id).emplace(id, std::move(desc)); get_file_map(id).emplace(id, std::move(desc));
} }
@ -551,7 +561,6 @@ ArchiveManager::FileDescription *ArchiveManager::get_file_desc(ShardIdFull shard
auto it = f.find(id); auto it = f.find(id);
if (it != f.end()) { if (it != f.end()) {
if (it->second.deleted) { if (it->second.deleted) {
CHECK(!force);
return nullptr; return nullptr;
} }
if (force && !id.temp) { if (force && !id.temp) {
@ -574,7 +583,7 @@ ArchiveManager::FileDescription *ArchiveManager::add_file_desc(ShardIdFull shard
FileDescription desc{id, false}; FileDescription desc{id, false};
td::mkdir(db_root_ + id.path()).ensure(); td::mkdir(db_root_ + id.path()).ensure();
std::string prefix = PSTRING() << db_root_ << id.path() << id.name(); std::string prefix = PSTRING() << db_root_ << id.path() << id.name();
desc.file = td::actor::create_actor<ArchiveSlice>("slice", id.id, id.key, id.temp, db_root_); desc.file = td::actor::create_actor<ArchiveSlice>("slice", id.id, id.key, id.temp, false, db_root_);
if (!id.temp) { if (!id.temp) {
update_desc(desc, shard, seqno, ts, lt); update_desc(desc, shard, seqno, ts, lt);
} }
@ -648,9 +657,13 @@ ArchiveManager::FileDescription *ArchiveManager::get_file_desc_by_seqno(ShardIdF
for (auto it = f.rbegin(); it != f.rend(); it++) { for (auto it = f.rbegin(); it != f.rend(); it++) {
auto i = it->second.first_blocks.find(shard); auto i = it->second.first_blocks.find(shard);
if (i != it->second.first_blocks.end() && i->second.seqno <= seqno) { if (i != it->second.first_blocks.end() && i->second.seqno <= seqno) {
if (it->second.deleted) {
return nullptr;
} else {
return &it->second; return &it->second;
} }
} }
}
return nullptr; return nullptr;
} }
@ -660,9 +673,13 @@ ArchiveManager::FileDescription *ArchiveManager::get_file_desc_by_unix_time(Shar
for (auto it = f.rbegin(); it != f.rend(); it++) { for (auto it = f.rbegin(); it != f.rend(); it++) {
auto i = it->second.first_blocks.find(shard); auto i = it->second.first_blocks.find(shard);
if (i != it->second.first_blocks.end() && i->second.ts <= ts) { if (i != it->second.first_blocks.end() && i->second.ts <= ts) {
if (it->second.deleted) {
return nullptr;
} else {
return &it->second; return &it->second;
} }
} }
}
return nullptr; return nullptr;
} }
@ -672,9 +689,13 @@ ArchiveManager::FileDescription *ArchiveManager::get_file_desc_by_lt(ShardIdFull
for (auto it = f.rbegin(); it != f.rend(); it++) { for (auto it = f.rbegin(); it != f.rend(); it++) {
auto i = it->second.first_blocks.find(shard); auto i = it->second.first_blocks.find(shard);
if (i != it->second.first_blocks.end() && i->second.lt <= lt) { if (i != it->second.first_blocks.end() && i->second.lt <= lt) {
if (it->second.deleted) {
return nullptr;
} else {
return &it->second; return &it->second;
} }
} }
}
return nullptr; return nullptr;
} }
@ -823,6 +844,14 @@ void ArchiveManager::start_up() {
} }
} }
v = index_->get("finalizedupto", value);
v.ensure();
if (v.move_as_ok() == td::KeyValue::GetStatus::Ok) {
auto R = td::to_integer_safe<td::uint32>(value);
R.ensure();
finalized_up_to_ = R.move_as_ok();
}
td::WalkPath::run(db_root_ + "/archive/states/", [&](td::CSlice fname, td::WalkPath::Type t) -> void { td::WalkPath::run(db_root_ + "/archive/states/", [&](td::CSlice fname, td::WalkPath::Type t) -> void {
if (t == td::WalkPath::Type::NotDir) { if (t == td::WalkPath::Type::NotDir) {
LOG(ERROR) << "checking file " << fname; LOG(ERROR) << "checking file " << fname;
@ -853,7 +882,7 @@ void ArchiveManager::start_up() {
persistent_state_gc(FileHash::zero()); persistent_state_gc(FileHash::zero());
} }
void ArchiveManager::run_gc(UnixTime ts) { void ArchiveManager::run_gc(UnixTime ts, UnixTime archive_ttl) {
auto p = get_temp_package_id_by_unixtime(ts); auto p = get_temp_package_id_by_unixtime(ts);
std::vector<PackageId> vec; std::vector<PackageId> vec;
for (auto &x : temp_files_) { for (auto &x : temp_files_) {
@ -863,14 +892,38 @@ void ArchiveManager::run_gc(UnixTime ts) {
break; break;
} }
} }
if (vec.size() <= 1) { if (vec.size() > 1) {
return;
}
vec.resize(vec.size() - 1, PackageId::empty(false, true)); vec.resize(vec.size() - 1, PackageId::empty(false, true));
for (auto &x : vec) { for (auto &x : vec) {
delete_package(x, [](td::Unit) {}); delete_package(x, [](td::Unit) {});
} }
}
vec.clear();
if (archive_ttl > 0) {
for (auto &f : files_) {
auto &desc = f.second;
if (desc.deleted) {
continue;
}
auto it = desc.first_blocks.find(ShardIdFull{masterchainId});
if (it == desc.first_blocks.end()) {
continue;
}
if (it->second.ts < ts - archive_ttl) {
vec.push_back(f.first);
}
}
if (vec.size() > 1) {
vec.resize(vec.size() - 1, PackageId::empty(false, true));
for (auto &x : vec) {
LOG(ERROR) << "WARNING: deleting package " << x.id;
delete_package(x, [](td::Unit) {});
}
}
}
} }
void ArchiveManager::persistent_state_gc(FileHash last) { void ArchiveManager::persistent_state_gc(FileHash last) {
@ -1049,6 +1102,102 @@ void ArchiveManager::set_async_mode(bool mode, td::Promise<td::Unit> promise) {
} }
} }
void ArchiveManager::truncate(BlockSeqno masterchain_seqno, ConstBlockHandle handle, td::Promise<td::Unit> promise) {
index_->begin_transaction().ensure();
td::MultiPromise mp;
auto ig = mp.init_guard();
ig.add_promise(std::move(promise));
for (auto &x : temp_files_) {
if (!x.second.deleted) {
td::actor::send_closure(x.second.file_actor_id(), &ArchiveSlice::destroy, ig.get_promise());
x.second.file.release();
}
}
temp_files_.clear();
{
auto it = key_files_.begin();
while (it != key_files_.end()) {
if (it->first.id <= masterchain_seqno) {
td::actor::send_closure(it->second.file_actor_id(), &ArchiveSlice::truncate, masterchain_seqno, handle,
ig.get_promise());
it++;
} else {
auto it2 = it;
it++;
td::actor::send_closure(it2->second.file_actor_id(), &ArchiveSlice::destroy, ig.get_promise());
it2->second.file.release();
index_
->erase(create_serialize_tl_object<ton_api::db_files_package_key>(it2->second.id.id, it2->second.id.key,
it2->second.id.temp)
.as_slice())
.ensure();
key_files_.erase(it2);
}
}
}
{
auto it = files_.begin();
while (it != files_.end()) {
if (it->first.id <= masterchain_seqno) {
td::actor::send_closure(it->second.file_actor_id(), &ArchiveSlice::truncate, masterchain_seqno, handle,
ig.get_promise());
it++;
} else {
auto it2 = it;
it++;
td::actor::send_closure(it2->second.file_actor_id(), &ArchiveSlice::destroy, ig.get_promise());
it2->second.file.release();
index_
->erase(create_serialize_tl_object<ton_api::db_files_package_key>(it2->second.id.id, it2->second.id.key,
it2->second.id.temp)
.as_slice())
.ensure();
files_.erase(it2);
}
}
}
{
std::vector<td::int32> t;
std::vector<td::int32> tk;
std::vector<td::int32> tt;
for (auto &e : files_) {
t.push_back(e.first.id);
}
for (auto &e : key_files_) {
tk.push_back(e.first.id);
}
for (auto &e : temp_files_) {
tt.push_back(e.first.id);
}
index_
->set(create_serialize_tl_object<ton_api::db_files_index_key>().as_slice(),
create_serialize_tl_object<ton_api::db_files_index_value>(std::move(t), std::move(tk), std::move(tt))
.as_slice())
.ensure();
}
index_->commit_transaction().ensure();
{
auto it = perm_states_.begin();
while (it != perm_states_.end()) {
int res = 0;
it->second.ref().visit(td::overloaded(
[&](const fileref::ZeroStateShort &x) { res = -1; },
[&](const fileref::PersistentStateShort &x) { res = x.masterchain_seqno <= masterchain_seqno ? -1 : 1; },
[&](const auto &obj) { res = 1; }));
if (res <= 0) {
it++;
} else {
auto it2 = it;
it++;
td::unlink(db_root_ + "/archive/states/" + it2->second.filename_short()).ignore();
perm_states_.erase(it2);
}
}
}
}
} // namespace validator } // namespace validator
} // namespace ton } // namespace ton

View file

@ -52,10 +52,10 @@ class ArchiveManager : public td::actor::Actor {
void check_persistent_state(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise<bool> promise); void check_persistent_state(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise<bool> promise);
void check_zero_state(BlockIdExt block_id, td::Promise<bool> promise); void check_zero_state(BlockIdExt block_id, td::Promise<bool> promise);
//void truncate(BlockSeqno masterchain_seqno, td::Promise<td::Unit> promise); void truncate(BlockSeqno masterchain_seqno, ConstBlockHandle handle, td::Promise<td::Unit> promise);
//void truncate_continue(BlockSeqno masterchain_seqno, td::Promise<td::Unit> promise); //void truncate_continue(BlockSeqno masterchain_seqno, td::Promise<td::Unit> promise);
void run_gc(UnixTime ts); void run_gc(UnixTime ts, UnixTime archive_ttl);
/* from LTDB */ /* from LTDB */
void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise<ConstBlockHandle> promise); void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise<ConstBlockHandle> promise);
@ -104,6 +104,7 @@ class ArchiveManager : public td::actor::Actor {
std::map<PackageId, FileDescription> files_; std::map<PackageId, FileDescription> files_;
std::map<PackageId, FileDescription> key_files_; std::map<PackageId, FileDescription> key_files_;
std::map<PackageId, FileDescription> temp_files_; std::map<PackageId, FileDescription> temp_files_;
BlockSeqno finalized_up_to_{0};
bool async_mode_ = false; bool async_mode_ = false;
bool huge_transaction_started_ = false; bool huge_transaction_started_ = false;
td::uint32 huge_transaction_size_ = 0; td::uint32 huge_transaction_size_ = 0;

View file

@ -309,7 +309,7 @@ void ArchiveSlice::get_block_common(AccountIdPrefixFull account_id,
} }
} }
f = true; f = true;
auto G = fetch_tl_object<ton_api::db_lt_desc_value>(td::BufferSlice{value}, true); auto G = fetch_tl_object<ton_api::db_lt_desc_value>(value, true);
G.ensure(); G.ensure();
auto g = G.move_as_ok(); auto g = G.move_as_ok();
if (compare_desc(*g.get()) > 0) { if (compare_desc(*g.get()) > 0) {
@ -467,12 +467,18 @@ void ArchiveSlice::start_up() {
R2 = kv_->get(PSTRING() << "status." << i, value); R2 = kv_->get(PSTRING() << "status." << i, value);
R2.ensure(); R2.ensure();
auto len = td::to_integer<td::uint64>(value); auto len = td::to_integer<td::uint64>(value);
R2 = kv_->get(PSTRING() << "version." << i, value);
R2.ensure();
td::uint32 ver = 0;
if (R2.move_as_ok() == td::KeyValue::GetStatus::Ok) {
ver = td::to_integer<td::uint32>(value);
}
auto v = archive_id_ + slice_size_ * i; auto v = archive_id_ + slice_size_ * i;
add_package(v, len); add_package(v, len, ver);
} }
} else { } else {
auto len = td::to_integer<td::uint64>(value); auto len = td::to_integer<td::uint64>(value);
add_package(archive_id_, len); add_package(archive_id_, len, 0);
} }
} else { } else {
if (!temp_ && !key_blocks_only_) { if (!temp_ && !key_blocks_only_) {
@ -482,13 +488,15 @@ void ArchiveSlice::start_up() {
kv_->set("slices", "1").ensure(); kv_->set("slices", "1").ensure();
kv_->set("slice_size", td::to_string(slice_size_)).ensure(); kv_->set("slice_size", td::to_string(slice_size_)).ensure();
kv_->set("status.0", "0").ensure(); kv_->set("status.0", "0").ensure();
kv_->set("version.0", td::to_string(default_package_version())).ensure();
kv_->commit_transaction().ensure(); kv_->commit_transaction().ensure();
add_package(archive_id_, 0, default_package_version());
} else { } else {
kv_->begin_transaction().ensure(); kv_->begin_transaction().ensure();
kv_->set("status", "0").ensure(); kv_->set("status", "0").ensure();
kv_->commit_transaction().ensure(); kv_->commit_transaction().ensure();
add_package(archive_id_, 0, 0);
} }
add_package(archive_id_, 0);
} }
} }
@ -528,8 +536,12 @@ void ArchiveSlice::set_async_mode(bool mode, td::Promise<td::Unit> promise) {
} }
} }
ArchiveSlice::ArchiveSlice(td::uint32 archive_id, bool key_blocks_only, bool temp, std::string db_root) ArchiveSlice::ArchiveSlice(td::uint32 archive_id, bool key_blocks_only, bool temp, bool finalized, std::string db_root)
: archive_id_(archive_id), key_blocks_only_(key_blocks_only), temp_(temp), db_root_(std::move(db_root)) { : archive_id_(archive_id)
, key_blocks_only_(key_blocks_only)
, temp_(temp)
, finalized_(finalized)
, db_root_(std::move(db_root)) {
} }
td::Result<ArchiveSlice::PackageInfo *> ArchiveSlice::choose_package(BlockSeqno masterchain_seqno, bool force) { td::Result<ArchiveSlice::PackageInfo *> ArchiveSlice::choose_package(BlockSeqno masterchain_seqno, bool force) {
@ -548,16 +560,17 @@ td::Result<ArchiveSlice::PackageInfo *> ArchiveSlice::choose_package(BlockSeqno
begin_transaction(); begin_transaction();
kv_->set("slices", td::to_string(v + 1)).ensure(); kv_->set("slices", td::to_string(v + 1)).ensure();
kv_->set(PSTRING() << "status." << v, "0").ensure(); kv_->set(PSTRING() << "status." << v, "0").ensure();
kv_->set(PSTRING() << "version." << v, td::to_string(default_package_version())).ensure();
commit_transaction(); commit_transaction();
CHECK((masterchain_seqno - archive_id_) % slice_size_ == 0); CHECK((masterchain_seqno - archive_id_) % slice_size_ == 0);
add_package(masterchain_seqno, 0); add_package(masterchain_seqno, 0, default_package_version());
return &packages_[v]; return &packages_[v];
} else { } else {
return &packages_[v]; return &packages_[v];
} }
} }
void ArchiveSlice::add_package(td::uint32 seqno, td::uint64 size) { void ArchiveSlice::add_package(td::uint32 seqno, td::uint64 size, td::uint32 version) {
PackageId p_id{seqno, key_blocks_only_, temp_}; PackageId p_id{seqno, key_blocks_only_, temp_};
std::string path = PSTRING() << db_root_ << p_id.path() << p_id.name() << ".pack"; std::string path = PSTRING() << db_root_ << p_id.path() << p_id.name() << ".pack";
auto R = Package::open(path, false, true); auto R = Package::open(path, false, true);
@ -565,9 +578,17 @@ void ArchiveSlice::add_package(td::uint32 seqno, td::uint64 size) {
LOG(FATAL) << "failed to open/create archive '" << path << "': " << R.move_as_error(); LOG(FATAL) << "failed to open/create archive '" << path << "': " << R.move_as_error();
return; return;
} }
auto idx = td::narrow_cast<td::uint32>(packages_.size());
if (finalized_) {
packages_.emplace_back(nullptr, td::actor::ActorOwn<PackageWriter>(), seqno, path, idx, version);
return;
}
auto pack = std::make_shared<Package>(R.move_as_ok()); auto pack = std::make_shared<Package>(R.move_as_ok());
if (version >= 1) {
pack->truncate(size).ensure();
}
auto writer = td::actor::create_actor<PackageWriter>("writer", pack); auto writer = td::actor::create_actor<PackageWriter>("writer", pack);
packages_.emplace_back(std::move(pack), std::move(writer), seqno, path, 0); packages_.emplace_back(std::move(pack), std::move(writer), seqno, path, idx, version);
} }
namespace { namespace {
@ -609,6 +630,207 @@ void ArchiveSlice::destroy(td::Promise<td::Unit> promise) {
td::Timestamp::in(0.0)); td::Timestamp::in(0.0));
} }
BlockSeqno ArchiveSlice::max_masterchain_seqno() {
auto key = get_db_key_lt_desc(ShardIdFull{masterchainId});
std::string value;
auto F = kv_->get(key, value);
F.ensure();
if (F.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
return 0;
}
auto G = fetch_tl_object<ton_api::db_lt_desc_value>(value, true);
G.ensure();
auto g = G.move_as_ok();
if (g->first_idx_ == g->last_idx_) {
return 0;
}
auto last_idx = g->last_idx_ - 1;
auto db_key = get_db_key_lt_el(ShardIdFull{masterchainId}, last_idx);
F = kv_->get(db_key, value);
F.ensure();
CHECK(F.move_as_ok() == td::KeyValue::GetStatus::Ok);
auto E = fetch_tl_object<ton_api::db_lt_el_value>(td::BufferSlice{value}, true);
E.ensure();
auto e = E.move_as_ok();
return e->id_->seqno_;
}
void ArchiveSlice::delete_file(FileReference ref_id) {
std::string value;
auto R = kv_->get(ref_id.hash().to_hex(), value);
R.ensure();
if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
return;
}
kv_->erase(ref_id.hash().to_hex());
}
void ArchiveSlice::delete_handle(ConstBlockHandle handle) {
delete_file(fileref::Proof{handle->id()});
delete_file(fileref::ProofLink{handle->id()});
delete_file(fileref::Block{handle->id()});
kv_->erase(get_db_key_block_info(handle->id()));
}
void ArchiveSlice::move_file(FileReference ref_id, Package *old_pack, Package *pack) {
LOG(DEBUG) << "moving " << ref_id.filename_short();
std::string value;
auto R = kv_->get(ref_id.hash().to_hex(), value);
R.ensure();
if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
return;
}
auto offset = td::to_integer<td::uint64>(value);
auto V = old_pack->read(offset);
V.ensure();
auto data = std::move(V.move_as_ok().second);
auto r = pack->append(ref_id.filename(), std::move(data), false);
kv_->set(ref_id.hash().to_hex(), td::to_string(r));
}
void ArchiveSlice::move_handle(ConstBlockHandle handle, Package *old_pack, Package *pack) {
move_file(fileref::Proof{handle->id()}, old_pack, pack);
move_file(fileref::ProofLink{handle->id()}, old_pack, pack);
move_file(fileref::Block{handle->id()}, old_pack, pack);
}
bool ArchiveSlice::truncate_block(BlockSeqno masterchain_seqno, BlockIdExt block_id, td::uint32 cutoff_idx,
Package *pack) {
std::string value;
auto R = kv_->get(get_db_key_block_info(block_id), value);
R.ensure();
CHECK(R.move_as_ok() == td::KeyValue::GetStatus::Ok);
auto E = create_block_handle(value);
E.ensure();
auto handle = E.move_as_ok();
auto seqno = handle->id().is_masterchain() ? handle->id().seqno() : handle->masterchain_ref_block();
if (seqno > masterchain_seqno) {
delete_handle(std::move(handle));
return false;
}
auto S = choose_package(seqno, false);
S.ensure();
auto p = S.move_as_ok();
CHECK(p->idx <= cutoff_idx);
if (p->idx == cutoff_idx) {
move_handle(std::move(handle), p->package.get(), pack);
}
return true;
}
void ArchiveSlice::truncate_shard(BlockSeqno masterchain_seqno, ShardIdFull shard, td::uint32 cutoff_idx,
Package *pack) {
auto key = get_db_key_lt_desc(shard);
std::string value;
auto F = kv_->get(key, value);
F.ensure();
if (F.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
return;
}
auto G = fetch_tl_object<ton_api::db_lt_desc_value>(value, true);
G.ensure();
auto g = G.move_as_ok();
if (g->first_idx_ == g->last_idx_) {
return;
}
int new_last_idx = g->first_idx_;
for (int i = g->first_idx_; i < g->last_idx_; i++) {
auto db_key = get_db_key_lt_el(shard, i);
F = kv_->get(db_key, value);
F.ensure();
CHECK(F.move_as_ok() == td::KeyValue::GetStatus::Ok);
auto E = fetch_tl_object<ton_api::db_lt_el_value>(value, true);
E.ensure();
auto e = E.move_as_ok();
if (truncate_block(masterchain_seqno, create_block_id(e->id_), cutoff_idx, pack)) {
CHECK(new_last_idx == i);
new_last_idx = i + 1;
}
}
if (g->last_idx_ != new_last_idx) {
g->last_idx_ = new_last_idx;
kv_->set(key, serialize_tl_object(g, true)).ensure();
}
}
void ArchiveSlice::truncate(BlockSeqno masterchain_seqno, ConstBlockHandle handle, td::Promise<td::Unit> promise) {
if (temp_ || archive_id_ > masterchain_seqno) {
destroy(std::move(promise));
return;
}
LOG(INFO) << "TRUNCATE: slice " << archive_id_ << " maxseqno= " << max_masterchain_seqno()
<< " truncate_upto=" << masterchain_seqno;
if (max_masterchain_seqno() <= masterchain_seqno) {
promise.set_value(td::Unit());
return;
}
auto cutoff = choose_package(masterchain_seqno, false);
cutoff.ensure();
auto pack = cutoff.move_as_ok();
CHECK(pack);
auto pack_r = Package::open(pack->path + ".new", false, true);
pack_r.ensure();
auto new_package = std::make_shared<Package>(pack_r.move_as_ok());
new_package->truncate(0).ensure();
std::string value;
auto status_key = create_serialize_tl_object<ton_api::db_lt_status_key>();
auto R = kv_->get(status_key, value);
R.ensure();
auto F = fetch_tl_object<ton_api::db_lt_status_value>(value, true);
F.ensure();
auto f = F.move_as_ok();
kv_->begin_transaction().ensure();
for (int i = 0; i < f->total_shards_; i++) {
auto shard_key = create_serialize_tl_object<ton_api::db_lt_shard_key>(i);
R = kv_->get(shard_key, value);
R.ensure();
CHECK(R.move_as_ok() == td::KeyValue::GetStatus::Ok);
auto G = fetch_tl_object<ton_api::db_lt_shard_value>(value, true);
G.ensure();
auto g = G.move_as_ok();
truncate_shard(masterchain_seqno, ShardIdFull{g->workchain_, static_cast<td::uint64>(g->shard_)}, pack->idx,
new_package.get());
}
if (!sliced_mode_) {
kv_->set("status", td::to_string(new_package->size())).ensure();
} else {
kv_->set(PSTRING() << "status." << pack->idx, td::to_string(new_package->size())).ensure();
for (size_t i = pack->idx + 1; i < packages_.size(); i++) {
kv_->erase(PSTRING() << "status." << i);
kv_->erase(PSTRING() << "version." << i);
}
kv_->set("slices", td::to_string(pack->idx + 1));
}
pack->package = new_package;
pack->writer.reset();
td::unlink(pack->path).ensure();
td::rename(pack->path + ".new", pack->path).ensure();
pack->writer = td::actor::create_actor<PackageWriter>("writer", new_package);
for (auto idx = pack->idx + 1; idx < packages_.size(); idx++) {
td::unlink(packages_[idx].path).ensure();
}
packages_.erase(packages_.begin() + pack->idx + 1);
kv_->commit_transaction().ensure();
promise.set_value(td::Unit());
}
} // namespace validator } // namespace validator
} // namespace ton } // namespace ton

View file

@ -73,7 +73,7 @@ class PackageWriter : public td::actor::Actor {
class ArchiveSlice : public td::actor::Actor { class ArchiveSlice : public td::actor::Actor {
public: public:
ArchiveSlice(td::uint32 archive_id, bool key_blocks_only, bool temp, std::string db_root); ArchiveSlice(td::uint32 archive_id, bool key_blocks_only, bool temp, bool finalized, std::string db_root);
void get_archive_id(BlockSeqno masterchain_seqno, td::Promise<td::uint64> promise); void get_archive_id(BlockSeqno masterchain_seqno, td::Promise<td::uint64> promise);
@ -97,6 +97,7 @@ class ArchiveSlice : public td::actor::Actor {
void start_up() override; void start_up() override;
void destroy(td::Promise<td::Unit> promise); void destroy(td::Promise<td::Unit> promise);
void truncate(BlockSeqno masterchain_seqno, ConstBlockHandle handle, td::Promise<td::Unit> promise);
void begin_transaction(); void begin_transaction();
void commit_transaction(); void commit_transaction();
@ -117,6 +118,7 @@ class ArchiveSlice : public td::actor::Actor {
bool key_blocks_only_; bool key_blocks_only_;
bool temp_; bool temp_;
bool finalized_;
bool destroyed_ = false; bool destroyed_ = false;
bool async_mode_ = false; bool async_mode_ = false;
@ -130,19 +132,38 @@ class ArchiveSlice : public td::actor::Actor {
struct PackageInfo { struct PackageInfo {
PackageInfo(std::shared_ptr<Package> package, td::actor::ActorOwn<PackageWriter> writer, BlockSeqno id, PackageInfo(std::shared_ptr<Package> package, td::actor::ActorOwn<PackageWriter> writer, BlockSeqno id,
std::string path, td::uint32 idx) std::string path, td::uint32 idx, td::uint32 version)
: package(std::move(package)), writer(std ::move(writer)), id(id), path(std::move(path)), idx(idx) { : package(std::move(package))
, writer(std ::move(writer))
, id(id)
, path(std::move(path))
, idx(idx)
, version(version) {
} }
std::shared_ptr<Package> package; std::shared_ptr<Package> package;
td::actor::ActorOwn<PackageWriter> writer; td::actor::ActorOwn<PackageWriter> writer;
BlockSeqno id; BlockSeqno id;
std::string path; std::string path;
td::uint32 idx; td::uint32 idx;
td::uint32 version;
}; };
std::vector<PackageInfo> packages_; std::vector<PackageInfo> packages_;
td::Result<PackageInfo *> choose_package(BlockSeqno masterchain_seqno, bool force); td::Result<PackageInfo *> choose_package(BlockSeqno masterchain_seqno, bool force);
void add_package(BlockSeqno masterchain_seqno, td::uint64 size); void add_package(BlockSeqno masterchain_seqno, td::uint64 size, td::uint32 version);
void truncate_shard(BlockSeqno masterchain_seqno, ShardIdFull shard, td::uint32 cutoff_idx, Package *pack);
bool truncate_block(BlockSeqno masterchain_seqno, BlockIdExt block_id, td::uint32 cutoff_idx, Package *pack);
void delete_handle(ConstBlockHandle handle);
void delete_file(FileReference ref_id);
void move_handle(ConstBlockHandle handle, Package *old_pack, Package *pack);
void move_file(FileReference ref_id, Package *old_pack, Package *pack);
BlockSeqno max_masterchain_seqno();
static constexpr td::uint32 default_package_version() {
return 1;
}
}; };
} // namespace validator } // namespace validator

View file

@ -44,9 +44,9 @@ void CellDbIn::start_up() {
auto empty = get_empty_key_hash(); auto empty = get_empty_key_hash();
if (get_block(empty).is_error()) { if (get_block(empty).is_error()) {
DbEntry e{get_empty_key(), empty, empty, RootHash::zero()}; DbEntry e{get_empty_key(), empty, empty, RootHash::zero()};
cell_db_->begin_transaction().ensure(); cell_db_->begin_write_batch().ensure();
set_block(empty, std::move(e)); set_block(empty, std::move(e));
cell_db_->commit_transaction().ensure(); cell_db_->commit_write_batch().ensure();
} }
last_gc_ = empty; last_gc_ = empty;
} }
@ -89,12 +89,12 @@ void CellDbIn::store_cell(BlockIdExt block_id, td::Ref<vm::Cell> cell, td::Promi
boc_->inc(cell); boc_->inc(cell);
boc_->prepare_commit().ensure(); boc_->prepare_commit().ensure();
vm::CellStorer stor{*cell_db_.get()}; vm::CellStorer stor{*cell_db_.get()};
cell_db_->begin_transaction().ensure(); cell_db_->begin_write_batch().ensure();
boc_->commit(stor).ensure(); boc_->commit(stor).ensure();
set_block(empty, std::move(E)); set_block(empty, std::move(E));
set_block(D.prev, std::move(P)); set_block(D.prev, std::move(P));
set_block(key_hash, std::move(D)); set_block(key_hash, std::move(D));
cell_db_->commit_transaction().ensure(); cell_db_->commit_write_batch().ensure();
boc_->set_loader(std::make_unique<vm::CellLoader>(cell_db_->snapshot())).ensure(); boc_->set_loader(std::make_unique<vm::CellLoader>(cell_db_->snapshot())).ensure();
td::actor::send_closure(parent_, &CellDb::update_snapshot, cell_db_->snapshot()); td::actor::send_closure(parent_, &CellDb::update_snapshot, cell_db_->snapshot());
@ -181,12 +181,12 @@ void CellDbIn::gc_cont2(BlockHandle handle) {
boc_->dec(cell); boc_->dec(cell);
boc_->prepare_commit().ensure(); boc_->prepare_commit().ensure();
vm::CellStorer stor{*cell_db_.get()}; vm::CellStorer stor{*cell_db_.get()};
cell_db_->begin_transaction().ensure(); cell_db_->begin_write_batch().ensure();
boc_->commit(stor).ensure(); boc_->commit(stor).ensure();
cell_db_->erase(get_key(last_gc_)).ensure(); cell_db_->erase(get_key(last_gc_)).ensure();
set_block(F.prev, std::move(P)); set_block(F.prev, std::move(P));
set_block(F.next, std::move(N)); set_block(F.next, std::move(N));
cell_db_->commit_transaction().ensure(); cell_db_->commit_write_batch().ensure();
alarm_timestamp() = td::Timestamp::now(); alarm_timestamp() = td::Timestamp::now();
boc_->set_loader(std::make_unique<vm::CellLoader>(cell_db_->snapshot())).ensure(); boc_->set_loader(std::make_unique<vm::CellLoader>(cell_db_->snapshot())).ensure();

View file

@ -409,10 +409,13 @@ void RootDb::prepare_stats(td::Promise<std::vector<std::pair<std::string, std::s
auto merger = StatsMerger::create(std::move(promise)); auto merger = StatsMerger::create(std::move(promise));
} }
void RootDb::truncate(td::Ref<MasterchainState> state, td::Promise<td::Unit> promise) { void RootDb::truncate(BlockSeqno seqno, ConstBlockHandle handle, td::Promise<td::Unit> promise) {
td::MultiPromise mp; td::MultiPromise mp;
auto ig = mp.init_guard(); auto ig = mp.init_guard();
ig.add_promise(std::move(promise)); ig.add_promise(std::move(promise));
td::actor::send_closure(archive_db_, &ArchiveManager::truncate, seqno, handle, ig.get_promise());
td::actor::send_closure(state_db_, &StateDb::truncate, seqno, handle, ig.get_promise());
} }
void RootDb::add_key_block_proof(td::Ref<Proof> proof, td::Promise<td::Unit> promise) { void RootDb::add_key_block_proof(td::Ref<Proof> proof, td::Promise<td::Unit> promise) {
@ -483,8 +486,8 @@ void RootDb::set_async_mode(bool mode, td::Promise<td::Unit> promise) {
td::actor::send_closure(archive_db_, &ArchiveManager::set_async_mode, mode, std::move(promise)); td::actor::send_closure(archive_db_, &ArchiveManager::set_async_mode, mode, std::move(promise));
} }
void RootDb::run_gc(UnixTime ts) { void RootDb::run_gc(UnixTime ts, UnixTime archive_ttl) {
td::actor::send_closure(archive_db_, &ArchiveManager::run_gc, ts); td::actor::send_closure(archive_db_, &ArchiveManager::run_gc, ts, archive_ttl);
} }
} // namespace validator } // namespace validator

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