diff --git a/.github/workflows/build-ton-linux-android-tonlib.yml b/.github/workflows/build-ton-linux-android-tonlib.yml index bbd956610..a1a6bc99d 100644 --- a/.github/workflows/build-ton-linux-android-tonlib.yml +++ b/.github/workflows/build-ton-linux-android-tonlib.yml @@ -28,5 +28,5 @@ jobs: - name: Upload artifacts uses: actions/upload-artifact@master with: - name: tonlib-android + name: ton-android-tonlib path: artifacts diff --git a/.github/workflows/build-ton-linux-x86-64-shared.yml b/.github/workflows/build-ton-linux-x86-64-shared.yml index fcef8afd5..e28ecc742 100644 --- a/.github/workflows/build-ton-linux-x86-64-shared.yml +++ b/.github/workflows/build-ton-linux-x86-64-shared.yml @@ -21,6 +21,12 @@ jobs: sudo apt-get update sudo apt-get install -y build-essential git cmake ninja-build zlib1g-dev libsecp256k1-dev libmicrohttpd-dev libsodium-dev liblz4-dev libjemalloc-dev + - if: matrix.os == 'ubuntu-20.04' + run: | + sudo apt install -y manpages-dev software-properties-common + sudo add-apt-repository ppa:ubuntu-toolchain-r/test + sudo apt update && sudo apt install gcc-11 g++-11 + - if: matrix.os != 'ubuntu-24.04' run: | wget https://apt.llvm.org/llvm.sh diff --git a/.github/workflows/build-ton-macos-15-arm64-shared.yml b/.github/workflows/build-ton-macos-15-arm64-shared.yml new file mode 100644 index 000000000..8379a0b04 --- /dev/null +++ b/.github/workflows/build-ton-macos-15-arm64-shared.yml @@ -0,0 +1,25 @@ +name: MacOS-15 TON build (shared, arm64) + +on: [push,workflow_dispatch,workflow_call] + +jobs: + build: + runs-on: macos-15 + + steps: + - name: Check out repository + uses: actions/checkout@v3 + with: + submodules: 'recursive' + + - name: Build TON + run: | + cp assembly/native/build-macos-shared.sh . + chmod +x build-macos-shared.sh + ./build-macos-shared.sh -t -a + + - name: Upload artifacts + uses: actions/upload-artifact@master + with: + name: ton-binaries-macos-15 + path: artifacts diff --git a/.github/workflows/build-ton-macos-arm64-shared.yml b/.github/workflows/build-ton-macos-arm64-shared.yml index 2a68272cc..aadd23dd0 100644 --- a/.github/workflows/build-ton-macos-arm64-shared.yml +++ b/.github/workflows/build-ton-macos-arm64-shared.yml @@ -1,4 +1,4 @@ -name: MacOS TON build (shared, arm64) +name: MacOS-14 TON build (shared, arm64) on: [push,workflow_dispatch,workflow_call] diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 367dd6638..263bd9a43 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -83,6 +83,14 @@ jobs: workflow_conclusion: success skip_unpack: true + - name: Download Android Tonlib artifacts + uses: dawidd6/action-download-artifact@v2 + with: + workflow: build-ton-linux-android-tonlib.yml + path: artifacts + workflow_conclusion: success + skip_unpack: true + - name: Show all artifacts run: | tree artifacts @@ -501,3 +509,11 @@ jobs: file: artifacts/ton-wasm-binaries.zip asset_name: ton-wasm-binaries.zip tag: ${{ steps.tag.outputs.TAG }} + + - name: Upload Android Tonlib artifacts + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-android-tonlib.zip + asset_name: ton-android-tonlib.zip + tag: ${{ steps.tag.outputs.TAG }} diff --git a/.github/workflows/ton-ccpcheck.yml b/.github/workflows/ton-ccpcheck.yml index d2d8cf700..95bef5f3d 100644 --- a/.github/workflows/ton-ccpcheck.yml +++ b/.github/workflows/ton-ccpcheck.yml @@ -20,7 +20,7 @@ jobs: generate report: true - name: Upload report - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@master with: name: ton-ccpcheck-report path: output diff --git a/CMake/FindSodium.cmake b/CMake/FindSodium.cmake index c11e46f1d..3818ef893 100644 --- a/CMake/FindSodium.cmake +++ b/CMake/FindSodium.cmake @@ -127,10 +127,16 @@ elseif (WIN32) endif() string(APPEND _PLATFORM_PATH "/$$CONFIG$$") + message(STATUS "MSVC_VERSION ${MSVC_VERSION}") if (MSVC_VERSION LESS 1900) math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 60") else() - math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 50") + if (MSVC_VERSION EQUAL 1941) + math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 51") + else() + math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 50") + endif() + endif() string(APPEND _PLATFORM_PATH "/v${_VS_VERSION}") diff --git a/CMakeLists.txt b/CMakeLists.txt index b92ff6f1b..573bc3a32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,7 +79,7 @@ else() set(HAVE_SSE42 FALSE) endif() -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) set(CMAKE_CXX_EXTENSIONS FALSE) @@ -333,6 +333,10 @@ add_cxx_compiler_flag("-Wno-sign-conversion") add_cxx_compiler_flag("-Qunused-arguments") add_cxx_compiler_flag("-Wno-unused-private-field") add_cxx_compiler_flag("-Wno-redundant-move") + +#add_cxx_compiler_flag("-Wno-unused-function") +#add_cxx_compiler_flag("-Wno-unused-variable") +#add_cxx_compiler_flag("-Wno-shorten-64-to-32") #add_cxx_compiler_flag("-Werror") #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem /usr/include/c++/v1") @@ -484,10 +488,10 @@ target_link_libraries(test-net PRIVATE tdnet tdutils ${CMAKE_THREAD_LIBS_INIT}) #BEGIN tonlib add_executable(test-tonlib ${TONLIB_ONLINE_TEST_SOURCE}) -target_link_libraries(test-tonlib tdutils tdactor adnllite tl_api ton_crypto ton_block tl_tonlib_api tonlib) +target_link_libraries(test-tonlib tdactor adnllite tl_api ton_crypto tl_tonlib_api tonlib) add_executable(test-tonlib-offline test/test-td-main.cpp ${TONLIB_OFFLINE_TEST_SOURCE}) -target_link_libraries(test-tonlib-offline tdutils tdactor adnllite tl_api ton_crypto ton_block fift-lib tl_tonlib_api tonlib) +target_link_libraries(test-tonlib-offline tdactor adnllite tl_api ton_crypto fift-lib tl_tonlib_api tonlib) if (NOT CMAKE_CROSSCOMPILING) add_dependencies(test-tonlib-offline gen_fif) @@ -529,6 +533,8 @@ target_link_libraries(test-rldp2 adnl adnltest dht rldp2 tl_api) add_executable(test-validator-session-state test/test-validator-session-state.cpp) target_link_libraries(test-validator-session-state adnl dht rldp validatorsession tl_api) +add_executable(test-overlay test/test-overlay.cpp) +target_link_libraries(test-overlay overlay tdutils tdactor adnl adnltest tl_api dht ) add_executable(test-catchain test/test-catchain.cpp) target_link_libraries(test-catchain overlay tdutils tdactor adnl adnltest rldp tl_api dht catchain ) diff --git a/Changelog.md b/Changelog.md index e5df2f979..c8a7ea257 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,19 @@ +## 2024.10 Update + +1. Parallel write to celldb: substantial improvement of sync and GC speed, especially with slow disks. +2. Decreased network traffic: only first block candidate is sent optimistically. +3. Improved channel creation and dht lookups, introduction of semi-private overlays +4. New LS dispatch queue related methods and improvement security +5. Fixing recursion in TVM continuations +6. Improved stats for actors, validator sessions, perf counters, overlays, adnl, rocksdb +7. Migration to C++20 +8. Improved block size estimates: account for depth in various structures +9. Fix bug with `<<` optimization in FunC +10. Minor changes of TVM which will be activated by `Config8.version >= 9` +11. Multiple minor improvements + +Besides the work of the core team, this update is based on the efforts of @krigga (emulator), Arayz @ TonBit (LS security, TVM recursion), @ret2happy (UB in BLST). + ## 2024.08 Update 1. Introduction of dispatch queues, message envelopes with transaction chain metadata, and explicitly stored msg_queue size, which will be activated by `Config8.version >= 8` and new `Config8.capabilities` bits: `capStoreOutMsgQueueSize`, `capMsgMetadata`, `capDeferMessages`. diff --git a/adnl/CMakeLists.txt b/adnl/CMakeLists.txt index 217a96247..111c4c500 100644 --- a/adnl/CMakeLists.txt +++ b/adnl/CMakeLists.txt @@ -88,17 +88,15 @@ target_link_libraries(adnl PUBLIC tdactor ton_crypto tl_api tdnet tddb keys keyr add_executable(adnl-proxy ${ADNL_PROXY_SOURCE}) target_include_directories(adnl-proxy PUBLIC $) -target_link_libraries(adnl-proxy PUBLIC tdactor ton_crypto tl_api tdnet common - tl-utils git) +target_link_libraries(adnl-proxy PUBLIC tdactor ton_crypto tl_api tdnet common tl-utils git) add_executable(adnl-pong adnl-pong.cpp) target_include_directories(adnl-pong PUBLIC $) -target_link_libraries(adnl-pong PUBLIC tdactor ton_crypto tl_api tdnet common - tl-utils adnl dht git) +target_link_libraries(adnl-pong PUBLIC tdactor ton_crypto tl_api tdnet common tl-utils adnl dht git) add_library(adnltest STATIC ${ADNL_TEST_SOURCE}) target_include_directories(adnltest PUBLIC $) -target_link_libraries(adnltest PUBLIC adnl ) +target_link_libraries(adnltest PUBLIC adnl) install(TARGETS adnl-proxy RUNTIME DESTINATION bin) endif() diff --git a/adnl/adnl-local-id.cpp b/adnl/adnl-local-id.cpp index d72fc7bcc..e0c62de76 100644 --- a/adnl/adnl-local-id.cpp +++ b/adnl/adnl-local-id.cpp @@ -306,7 +306,7 @@ void AdnlLocalId::update_packet(AdnlPacket packet, bool update_id, bool sign, td } } -void AdnlLocalId::get_stats(td::Promise> promise) { +void AdnlLocalId::get_stats(bool all, td::Promise> promise) { auto stats = create_tl_object(); stats->short_id_ = short_id_.bits256_value(); for (auto &[ip, x] : inbound_rate_limiter_) { @@ -317,7 +317,7 @@ void AdnlLocalId::get_stats(td::Promisepackets_recent_ = packet_stats_prev_.tl(); - stats->packets_total_ = packet_stats_total_.tl(); + stats->packets_total_ = packet_stats_total_.tl(all); stats->packets_total_->ts_start_ = (double)Adnl::adnl_start_time(); stats->packets_total_->ts_end_ = td::Clocks::system(); promise.set_result(std::move(stats)); @@ -325,14 +325,14 @@ void AdnlLocalId::get_stats(td::Promise AdnlLocalId::PacketStats::tl() const { +tl_object_ptr AdnlLocalId::PacketStats::tl(bool all) const { + double threshold = all ? -1.0 : td::Clocks::system() - 600.0; auto obj = create_tl_object(); obj->ts_start_ = ts_start; obj->ts_end_ = ts_end; for (const auto &[ip, packets] : decrypted_packets) { - obj->decrypted_packets_.push_back(create_tl_object( - ip.is_valid() ? PSTRING() << ip.get_ip_str() << ":" << ip.get_port() : "", packets)); + if (packets.last_packet_ts >= threshold) { + obj->decrypted_packets_.push_back(create_tl_object( + ip.is_valid() ? PSTRING() << ip.get_ip_str() << ":" << ip.get_port() : "", packets.packets)); + } } for (const auto &[ip, packets] : dropped_packets) { - obj->dropped_packets_.push_back(create_tl_object( - ip.is_valid() ? PSTRING() << ip.get_ip_str() << ":" << ip.get_port() : "", packets)); + if (packets.last_packet_ts >= threshold) { + obj->dropped_packets_.push_back(create_tl_object( + ip.is_valid() ? PSTRING() << ip.get_ip_str() << ":" << ip.get_port() : "", packets.packets)); + } } return obj; } diff --git a/adnl/adnl-local-id.h b/adnl/adnl-local-id.h index be9d79d2c..fa7f7f749 100644 --- a/adnl/adnl-local-id.h +++ b/adnl/adnl-local-id.h @@ -78,7 +78,7 @@ class AdnlLocalId : public td::actor::Actor { void update_packet(AdnlPacket packet, bool update_id, bool sign, td::int32 update_addr_list_if, td::int32 update_priority_addr_list_if, td::Promise promise); - void get_stats(td::Promise> promise); + void get_stats(bool all, td::Promise> promise); td::uint32 get_mode() { return mode_; @@ -111,10 +111,20 @@ class AdnlLocalId : public td::actor::Actor { std::map inbound_rate_limiter_; struct PacketStats { double ts_start = 0.0, ts_end = 0.0; - std::map decrypted_packets; - std::map dropped_packets; - tl_object_ptr tl() const; + struct Counter { + td::uint64 packets = 0; + double last_packet_ts = 0.0; + + void inc() { + ++packets; + last_packet_ts = td::Clocks::system(); + } + }; + std::map decrypted_packets; + std::map dropped_packets; + + tl_object_ptr tl(bool all = true) const; } packet_stats_cur_, packet_stats_prev_, packet_stats_total_; void add_decrypted_packet_stats(td::IPAddress addr); void add_dropped_packet_stats(td::IPAddress addr); diff --git a/adnl/adnl-peer-table.cpp b/adnl/adnl-peer-table.cpp index d885623a9..b8aab5671 100644 --- a/adnl/adnl-peer-table.cpp +++ b/adnl/adnl-peer-table.cpp @@ -385,7 +385,7 @@ void AdnlPeerTableImpl::get_conn_ip_str(AdnlNodeIdShort l_id, AdnlNodeIdShort p_ td::actor::send_closure(it->second, &AdnlPeer::get_conn_ip_str, l_id, std::move(promise)); } -void AdnlPeerTableImpl::get_stats(td::Promise> promise) { +void AdnlPeerTableImpl::get_stats(bool all, td::Promise> promise) { class Cb : public td::actor::Actor { public: explicit Cb(td::Promise> promise) : promise_(std::move(promise)) { @@ -440,7 +440,7 @@ void AdnlPeerTableImpl::get_stats(td::Promise for (auto &[id, local_id] : local_ids_) { td::actor::send_closure(callback, &Cb::inc_pending); - td::actor::send_closure(local_id.local_id, &AdnlLocalId::get_stats, + td::actor::send_closure(local_id.local_id, &AdnlLocalId::get_stats, all, [id = id, callback](td::Result> R) { if (R.is_error()) { VLOG(ADNL_NOTICE) @@ -454,7 +454,7 @@ void AdnlPeerTableImpl::get_stats(td::Promise for (auto &[id, peer] : peers_) { td::actor::send_closure(callback, &Cb::inc_pending); td::actor::send_closure( - peer, &AdnlPeer::get_stats, + peer, &AdnlPeer::get_stats, all, [id = id, callback](td::Result>> R) { if (R.is_error()) { VLOG(ADNL_NOTICE) << "failed to get stats for peer " << id << " : " << R.move_as_error(); diff --git a/adnl/adnl-peer-table.hpp b/adnl/adnl-peer-table.hpp index 12f64fcb2..9ad61b653 100644 --- a/adnl/adnl-peer-table.hpp +++ b/adnl/adnl-peer-table.hpp @@ -108,7 +108,7 @@ class AdnlPeerTableImpl : public AdnlPeerTable { td::Promise, AdnlAddress>> promise) override; void get_conn_ip_str(AdnlNodeIdShort l_id, AdnlNodeIdShort p_id, td::Promise promise) override; - void get_stats(td::Promise> promise) override; + void get_stats(bool all, td::Promise> promise) override; struct PrintId {}; PrintId print_id() const { diff --git a/adnl/adnl-peer.cpp b/adnl/adnl-peer.cpp index 2b0077a8e..7f5c60394 100644 --- a/adnl/adnl-peer.cpp +++ b/adnl/adnl-peer.cpp @@ -808,7 +808,15 @@ void AdnlPeerPairImpl::get_conn_ip_str(td::Promise promise) { promise.set_value("undefined"); } -void AdnlPeerPairImpl::get_stats(td::Promise> promise) { +void AdnlPeerPairImpl::get_stats(bool all, td::Promise> promise) { + if (!all) { + double threshold = td::Clocks::system() - 600.0; + if (last_in_packet_ts_ < threshold && last_out_packet_ts_ < threshold) { + promise.set_value(nullptr); + return; + } + } + auto stats = create_tl_object(); stats->local_id_ = local_id_.bits256_value(); stats->peer_id_ = peer_id_short_.bits256_value(); @@ -993,7 +1001,7 @@ void AdnlPeerImpl::update_addr_list(AdnlNodeIdShort local_id, td::uint32 local_m td::actor::send_closure(it->second, &AdnlPeerPair::update_addr_list, std::move(addr_list)); } -void AdnlPeerImpl::get_stats(td::Promise>> promise) { +void AdnlPeerImpl::get_stats(bool all, td::Promise>> promise) { class Cb : public td::actor::Actor { public: explicit Cb(td::Promise>> promise) @@ -1001,7 +1009,9 @@ void AdnlPeerImpl::get_stats(td::Promise peer_pair) { - result_.push_back(std::move(peer_pair)); + if (peer_pair) { + result_.push_back(std::move(peer_pair)); + } dec_pending(); } @@ -1027,7 +1037,7 @@ void AdnlPeerImpl::get_stats(td::Promise> R) { if (R.is_error()) { diff --git a/adnl/adnl-peer.h b/adnl/adnl-peer.h index b7d6adc0f..1215f71da 100644 --- a/adnl/adnl-peer.h +++ b/adnl/adnl-peer.h @@ -59,7 +59,7 @@ class AdnlPeerPair : public td::actor::Actor { virtual void update_peer_id(AdnlNodeIdFull id) = 0; virtual void update_addr_list(AdnlAddressList addr_list) = 0; virtual void get_conn_ip_str(td::Promise promise) = 0; - virtual void get_stats(td::Promise> promise) = 0; + virtual void get_stats(bool all, td::Promise> promise) = 0; static td::actor::ActorOwn create(td::actor::ActorId network_manager, td::actor::ActorId peer_table, td::uint32 local_mode, @@ -101,7 +101,7 @@ class AdnlPeer : public td::actor::Actor { td::actor::ActorId local_actor, AdnlAddressList addr_list) = 0; virtual void update_dht_node(td::actor::ActorId dht_node) = 0; virtual void get_conn_ip_str(AdnlNodeIdShort l_id, td::Promise promise) = 0; - virtual void get_stats(td::Promise>> promise) = 0; + virtual void get_stats(bool all, td::Promise>> promise) = 0; }; } // namespace adnl diff --git a/adnl/adnl-peer.hpp b/adnl/adnl-peer.hpp index d25a24cf4..7db2e2a1d 100644 --- a/adnl/adnl-peer.hpp +++ b/adnl/adnl-peer.hpp @@ -90,7 +90,7 @@ class AdnlPeerPairImpl : public AdnlPeerPair { void update_peer_id(AdnlNodeIdFull id) override; void get_conn_ip_str(td::Promise promise) override; - void get_stats(td::Promise> promise) override; + void get_stats(bool all, td::Promise> promise) override; void got_data_from_db(td::Result R); void got_data_from_static_nodes(td::Result R); @@ -302,7 +302,7 @@ class AdnlPeerImpl : public AdnlPeer { AdnlAddressList addr_list) override; void update_dht_node(td::actor::ActorId dht_node) override; void get_conn_ip_str(AdnlNodeIdShort l_id, td::Promise promise) override; - void get_stats(td::Promise>> promise) override; + void get_stats(bool all, td::Promise>> promise) override; //void check_signature(td::BufferSlice data, td::BufferSlice signature, td::Promise promise) override; AdnlPeerImpl(td::actor::ActorId network_manager, td::actor::ActorId peer_table, diff --git a/adnl/adnl-query.cpp b/adnl/adnl-query.cpp index e098c1344..89dbbfc49 100644 --- a/adnl/adnl-query.cpp +++ b/adnl/adnl-query.cpp @@ -25,7 +25,7 @@ namespace ton { namespace adnl { void AdnlQuery::alarm() { - set_error(td::Status::Error(ErrorCode::timeout, "adnl query timeout")); + set_error(td::Status::Error(ErrorCode::timeout, PSTRING() << "timeout for adnl query " << name_)); } void AdnlQuery::result(td::BufferSlice data) { promise_.set_value(std::move(data)); diff --git a/adnl/adnl.h b/adnl/adnl.h index a276e0c21..b49581a91 100644 --- a/adnl/adnl.h +++ b/adnl/adnl.h @@ -121,7 +121,7 @@ class Adnl : public AdnlSenderInterface { virtual void create_tunnel(AdnlNodeIdShort dst, td::uint32 size, td::Promise, AdnlAddress>> promise) = 0; - virtual void get_stats(td::Promise> promise) = 0; + virtual void get_stats(bool all, td::Promise> promise) = 0; static td::actor::ActorOwn create(std::string db, td::actor::ActorId keyring); diff --git a/assembly/native/build-macos-portable.sh b/assembly/native/build-macos-portable.sh index b296d3393..0e1003b56 100644 --- a/assembly/native/build-macos-portable.sh +++ b/assembly/native/build-macos-portable.sh @@ -24,7 +24,7 @@ else fi export NONINTERACTIVE=1 -brew install ninja pkg-config automake libtool autoconf +brew install ninja pkg-config automake libtool autoconf texinfo brew install llvm@16 @@ -88,7 +88,7 @@ if [ ! -d "openssl_3" ]; then cd openssl_3 opensslPath=`pwd` git checkout openssl-3.1.4 - ./config -static + ./config make build_libs -j12 test $? -eq 0 || { echo "Can't compile openssl_3"; exit 1; } cd .. @@ -168,23 +168,23 @@ else test $? -eq 0 || { echo "Can't compile ton"; exit 1; } fi -strip storage/storage-daemon/storage-daemon -strip storage/storage-daemon/storage-daemon-cli -strip blockchain-explorer/blockchain-explorer -strip crypto/fift -strip crypto/func -strip crypto/create-state -strip crypto/tlbc -strip validator-engine-console/validator-engine-console -strip tonlib/tonlib-cli -strip http/http-proxy -strip rldp-http-proxy/rldp-http-proxy -strip dht-server/dht-server -strip lite-client/lite-client -strip validator-engine/validator-engine -strip utils/generate-random-id -strip utils/json2tlo -strip adnl/adnl-proxy +strip -s storage/storage-daemon/storage-daemon +strip -s storage/storage-daemon/storage-daemon-cli +strip -s blockchain-explorer/blockchain-explorer +strip -s crypto/fift +strip -s crypto/func +strip -s crypto/create-state +strip -s crypto/tlbc +strip -s validator-engine-console/validator-engine-console +strip -s tonlib/tonlib-cli +strip -s http/http-proxy +strip -s rldp-http-proxy/rldp-http-proxy +strip -s dht-server/dht-server +strip -s lite-client/lite-client +strip -s validator-engine/validator-engine +strip -s utils/generate-random-id +strip -s utils/json2tlo +strip -s adnl/adnl-proxy cd .. @@ -192,8 +192,6 @@ if [ "$with_artifacts" = true ]; then echo Creating artifacts... rm -rf artifacts mkdir artifacts - cp crypto/fift/lib artifacts/ - cp -R crypto/smartcont/ artifacts/ cp build/storage/storage-daemon/storage-daemon artifacts/ cp build/storage/storage-daemon/storage-daemon-cli artifacts/ cp build/blockchain-explorer/blockchain-explorer artifacts/ @@ -213,9 +211,9 @@ if [ "$with_artifacts" = true ]; then cp build/utils/json2tlo artifacts/ cp build/adnl/adnl-proxy artifacts/ cp build/emulator/libemulator.dylib artifacts/ - chmod +x artifacts/* rsync -r crypto/smartcont artifacts/ rsync -r crypto/fift/lib artifacts/ + chmod -R +x artifacts/* fi if [ "$with_tests" = true ]; then diff --git a/assembly/native/build-macos-shared.sh b/assembly/native/build-macos-shared.sh index 7574f481a..7fdcfb941 100644 --- a/assembly/native/build-macos-shared.sh +++ b/assembly/native/build-macos-shared.sh @@ -97,23 +97,23 @@ else fi -strip storage/storage-daemon/storage-daemon -strip storage/storage-daemon/storage-daemon-cli -strip blockchain-explorer/blockchain-explorer -strip crypto/fift -strip crypto/func -strip crypto/create-state -strip crypto/tlbc -strip validator-engine-console/validator-engine-console -strip tonlib/tonlib-cli -strip http/http-proxy -strip rldp-http-proxy/rldp-http-proxy -strip dht-server/dht-server -strip lite-client/lite-client -strip validator-engine/validator-engine -strip utils/generate-random-id -strip utils/json2tlo -strip adnl/adnl-proxy +strip -s storage/storage-daemon/storage-daemon +strip -s storage/storage-daemon/storage-daemon-cli +strip -s blockchain-explorer/blockchain-explorer +strip -s crypto/fift +strip -s crypto/func +strip -s crypto/create-state +strip -s crypto/tlbc +strip -s validator-engine-console/validator-engine-console +strip -s tonlib/tonlib-cli +strip -s http/http-proxy +strip -s rldp-http-proxy/rldp-http-proxy +strip -s dht-server/dht-server +strip -s lite-client/lite-client +strip -s validator-engine/validator-engine +strip -s utils/generate-random-id +strip -s utils/json2tlo +strip -s adnl/adnl-proxy cd .. @@ -140,9 +140,9 @@ if [ "$with_artifacts" = true ]; then cp build/utils/json2tlo artifacts/ cp build/adnl/adnl-proxy artifacts/ cp build/emulator/libemulator.dylib artifacts/ - chmod +x artifacts/* - rsync -r crypto/smartcont artifacts/ - rsync -r crypto/fift/lib artifacts/ + cp -R crypto/smartcont artifacts/ + cp -R crypto/fift/lib artifacts/ + chmod -R +x artifacts/* fi if [ "$with_tests" = true ]; then diff --git a/assembly/native/build-ubuntu-portable.sh b/assembly/native/build-ubuntu-portable.sh index b5a167626..73ae59264 100644 --- a/assembly/native/build-ubuntu-portable.sh +++ b/assembly/native/build-ubuntu-portable.sh @@ -81,7 +81,7 @@ if [ ! -d "openssl_3" ]; then cd openssl_3 opensslPath=`pwd` git checkout openssl-3.1.4 - ./config -static + ./config make build_libs -j12 test $? -eq 0 || { echo "Can't compile openssl_3"; exit 1; } cd .. @@ -160,7 +160,7 @@ ninja storage-daemon storage-daemon-cli fift func tonlib tonlibjson tonlib-cli \ test $? -eq 0 || { echo "Can't compile ton"; exit 1; } fi -strip -g storage/storage-daemon/storage-daemon \ +strip -s storage/storage-daemon/storage-daemon \ storage/storage-daemon/storage-daemon-cli \ blockchain-explorer/blockchain-explorer \ crypto/fift \ @@ -193,8 +193,6 @@ cd .. if [ "$with_artifacts" = true ]; then rm -rf artifacts mkdir artifacts - cp crypto/fift/lib artifacts/ - cp -R crypto/smartcont/ artifacts/ mv build/tonlib/libtonlibjson.so.0.5 build/tonlib/libtonlibjson.so cp build/storage/storage-daemon/storage-daemon build/storage/storage-daemon/storage-daemon-cli \ build/crypto/fift build/crypto/tlbc build/crypto/func build/crypto/create-state build/blockchain-explorer/blockchain-explorer \ @@ -204,9 +202,9 @@ if [ "$with_artifacts" = true ]; then build/utils/generate-random-id build/utils/json2tlo build/adnl/adnl-proxy build/emulator/libemulator.so \ artifacts test $? -eq 0 || { echo "Can't copy final binaries"; exit 1; } - chmod +x artifacts/* cp -R crypto/smartcont artifacts cp -R crypto/fift/lib artifacts + chmod -R +x artifacts/* fi if [ "$with_tests" = true ]; then diff --git a/assembly/native/build-ubuntu-shared.sh b/assembly/native/build-ubuntu-shared.sh index 4ce86d81f..00b9aa9b4 100644 --- a/assembly/native/build-ubuntu-shared.sh +++ b/assembly/native/build-ubuntu-shared.sh @@ -68,7 +68,7 @@ ninja storage-daemon storage-daemon-cli fift func tonlib tonlibjson tonlib-cli \ test $? -eq 0 || { echo "Can't compile ton"; exit 1; } fi -strip -g storage/storage-daemon/storage-daemon \ +strip -s storage/storage-daemon/storage-daemon \ storage/storage-daemon/storage-daemon-cli \ blockchain-explorer/blockchain-explorer \ crypto/fift \ @@ -112,9 +112,9 @@ if [ "$with_artifacts" = true ]; then build/utils/generate-random-id build/utils/json2tlo build/adnl/adnl-proxy build/emulator/libemulator.so \ artifacts test $? -eq 0 || { echo "Can't copy final binaries"; exit 1; } - chmod +x artifacts/* cp -R crypto/smartcont artifacts cp -R crypto/fift/lib artifacts + chmod -R +x artifacts/* fi if [ "$with_tests" = true ]; then diff --git a/assembly/native/build-windows-2019.bat b/assembly/native/build-windows-2019.bat index b528b05a2..f728b88f8 100644 --- a/assembly/native/build-windows-2019.bat +++ b/assembly/native/build-windows-2019.bat @@ -216,6 +216,6 @@ build\validator-engine\validator-engine.exe ^ build\utils\generate-random-id.exe ^ build\utils\json2tlo.exe ^ build\adnl\adnl-proxy.exe ^ -build\emulator\emulator.dll) do (strip -g %%I & copy %%I artifacts\) +build\emulator\emulator.dll) do (strip -s %%I & copy %%I artifacts\) xcopy /e /k /h /i crypto\smartcont artifacts\smartcont xcopy /e /k /h /i crypto\fift\lib artifacts\lib diff --git a/assembly/native/build-windows.bat b/assembly/native/build-windows.bat index a9871aa96..aa0fd69ad 100644 --- a/assembly/native/build-windows.bat +++ b/assembly/native/build-windows.bat @@ -217,6 +217,6 @@ build\validator-engine\validator-engine.exe ^ build\utils\generate-random-id.exe ^ build\utils\json2tlo.exe ^ build\adnl\adnl-proxy.exe ^ -build\emulator\emulator.dll) do (strip -g %%I & copy %%I artifacts\) +build\emulator\emulator.dll) do (strip -s %%I & copy %%I artifacts\) xcopy /e /k /h /i crypto\smartcont artifacts\smartcont xcopy /e /k /h /i crypto\fift\lib artifacts\lib diff --git a/assembly/nix/build-linux-arm64-nix.sh b/assembly/nix/build-linux-arm64-nix.sh index 7e85a8712..2c7df521f 100644 --- a/assembly/nix/build-linux-arm64-nix.sh +++ b/assembly/nix/build-linux-arm64-nix.sh @@ -35,3 +35,23 @@ cp ./result/lib/libtonlibjson.so.0.5 artifacts/libtonlibjson.so cp ./result/lib/libemulator.so artifacts/ cp ./result/lib/fift/* artifacts/lib/ cp -r ./result/share/ton/smartcont artifacts/ +chmod -R +x artifacts +cd artifacts +sudo strip -s storage-daemon \ + storage-daemon-cli \ + blockchain-explorer \ + fift \ + tlbc \ + func \ + create-state \ + validator-engine-console \ + tonlib-cli \ + http-proxy \ + rldp-http-proxy \ + dht-server \ + lite-client \ + validator-engine \ + generate-random-id \ + adnl-proxy \ + libemulator.so \ + libtonlibjson.so diff --git a/assembly/nix/build-linux-x86-64-nix.sh b/assembly/nix/build-linux-x86-64-nix.sh index c1f1dcf37..ae478ec2d 100644 --- a/assembly/nix/build-linux-x86-64-nix.sh +++ b/assembly/nix/build-linux-x86-64-nix.sh @@ -35,3 +35,23 @@ cp ./result/lib/libtonlibjson.so.0.5 artifacts/libtonlibjson.so cp ./result/lib/libemulator.so artifacts/ cp ./result/lib/fift/* artifacts/lib/ cp -r ./result/share/ton/smartcont artifacts/ +chmod -R +x artifacts +cd artifacts +sudo strip -s storage-daemon \ + storage-daemon-cli \ + blockchain-explorer \ + fift \ + tlbc \ + func \ + create-state \ + validator-engine-console \ + tonlib-cli \ + http-proxy \ + rldp-http-proxy \ + dht-server \ + lite-client \ + validator-engine \ + generate-random-id \ + adnl-proxy \ + libemulator.so \ + libtonlibjson.so diff --git a/assembly/nix/build-macos-nix.sh b/assembly/nix/build-macos-nix.sh index c75ca0428..c92eddb28 100644 --- a/assembly/nix/build-macos-nix.sh +++ b/assembly/nix/build-macos-nix.sh @@ -35,3 +35,23 @@ cp ./result/lib/libtonlibjson.dylib artifacts/ cp ./result/lib/libemulator.dylib artifacts/ cp ./result/lib/fift/* artifacts/lib/ cp -r ./result/share/ton/smartcont artifacts/ +chmod -R +x artifacts +cd artifacts +sudo strip -xSX storage-daemon \ + storage-daemon-cli \ + blockchain-explorer \ + fift \ + tlbc \ + func \ + create-state \ + validator-engine-console \ + tonlib-cli \ + http-proxy \ + rldp-http-proxy \ + dht-server \ + lite-client \ + validator-engine \ + generate-random-id \ + adnl-proxy \ + libemulator.dylib \ + libtonlibjson.dylib diff --git a/assembly/nix/linux-arm64-tonlib.nix b/assembly/nix/linux-arm64-tonlib.nix index a051e34cd..9d251e020 100644 --- a/assembly/nix/linux-arm64-tonlib.nix +++ b/assembly/nix/linux-arm64-tonlib.nix @@ -42,10 +42,13 @@ pkgs.llvmPackages_16.stdenv.mkDerivation { ]; dontAddStaticConfigureFlags = false; + doCheck = false; + doInstallCheck = false; cmakeFlags = [ - "-DTON_USE_ABSEIL=OFF" + "-DTON_USE_ABSEIL=ON" "-DNIX=ON" + "-DTON_ONLY_TONLIB=ON" ]; LDFLAGS = [ diff --git a/assembly/nix/linux-x86-64-tonlib.nix b/assembly/nix/linux-x86-64-tonlib.nix index afcbe3ba6..64ceaaea2 100644 --- a/assembly/nix/linux-x86-64-tonlib.nix +++ b/assembly/nix/linux-x86-64-tonlib.nix @@ -58,10 +58,13 @@ stdenv227.mkDerivation { ]; dontAddStaticConfigureFlags = false; + doCheck = false; + doInstallCheck = false; cmakeFlags = [ - "-DTON_USE_ABSEIL=OFF" + "-DTON_USE_ABSEIL=ON" "-DNIX=ON" + "-DTON_ONLY_TONLIB=ON" ]; LDFLAGS = [ diff --git a/blockchain-explorer/CMakeLists.txt b/blockchain-explorer/CMakeLists.txt index 8aae88058..36d4e5069 100644 --- a/blockchain-explorer/CMakeLists.txt +++ b/blockchain-explorer/CMakeLists.txt @@ -16,26 +16,26 @@ add_executable(blockchain-explorer ${BLOCHAIN_EXPLORER_SOURCE}) if (NIX) if (MHD_FOUND) target_include_directories(blockchain-explorer PUBLIC ${MHD_INCLUDE_DIR}) - target_link_libraries(blockchain-explorer tdutils tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ton_block ${MHD_LIBRARY}) + target_link_libraries(blockchain-explorer tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ${MHD_LIBRARY}) else() find_package(PkgConfig REQUIRED) pkg_check_modules(MHD libmicrohttpd) target_include_directories(blockchain-explorer PUBLIC ${MHD_INCLUDE_DIR} ${MHD_STATIC_INCLUDE_DIRS}) - target_link_libraries(blockchain-explorer tdutils tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ton_block ${MHD_LIBRARIES} ${MHD_STATIC_LIBRARIES}) + target_link_libraries(blockchain-explorer tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ${MHD_LIBRARIES} ${MHD_STATIC_LIBRARIES}) endif() else() if (MHD_FOUND) target_include_directories(blockchain-explorer PUBLIC ${MHD_INCLUDE_DIR}) - target_link_libraries(blockchain-explorer tdutils tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ton_block ${MHD_LIBRARY}) + target_link_libraries(blockchain-explorer tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ${MHD_LIBRARY}) else() find_package(MHD) target_include_directories(blockchain-explorer PUBLIC ${MHD_INCLUDE_DIR}) - target_link_libraries(blockchain-explorer tdutils tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ton_block ${MHD_LIBRARY}) + target_link_libraries(blockchain-explorer tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ${MHD_LIBRARY}) endif() endif() target_include_directories(blockchain-explorer PUBLIC ${MHD_INCLUDE_DIR}) -target_link_libraries(blockchain-explorer tdutils tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ton_block ${MHD_LIBRARY}) +target_link_libraries(blockchain-explorer tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ${MHD_LIBRARY}) install(TARGETS blockchain-explorer RUNTIME DESTINATION bin) diff --git a/blockchain-explorer/blockchain-explorer-http.cpp b/blockchain-explorer/blockchain-explorer-http.cpp index e2322ff76..e5203cb7e 100644 --- a/blockchain-explorer/blockchain-explorer-http.cpp +++ b/blockchain-explorer/blockchain-explorer-http.cpp @@ -122,9 +122,8 @@ HttpAnswer& HttpAnswer::operator<<(MessageCell msg) { abort("cannot unpack internal message"); return *this; } - td::RefInt256 value; - td::Ref extra; - if (!block::unpack_CurrencyCollection(info.value, value, extra)) { + block::CurrencyCollection currency_collection; + if (!currency_collection.unpack(info.value)) { abort("cannot unpack message value"); return *this; } @@ -133,7 +132,7 @@ HttpAnswer& HttpAnswer::operator<<(MessageCell msg) { << "destination" << AddressCell{info.dest} << "\n" << "lt" << info.created_lt << "\n" << "time" << info.created_at << " (" << time_to_human(info.created_at) << ")\n" - << "value" << value << "\n"; + << "value" << currency_collection.to_str()<< "\n"; break; } default: @@ -365,6 +364,7 @@ HttpAnswer& HttpAnswer::operator<<(AccountCell acc_c) { ton::LogicalTime last_trans_lt = 0; ton::Bits256 last_trans_hash; last_trans_hash.set_zero(); + block::CurrencyCollection balance = block::CurrencyCollection::zero(); try { auto state_root = vm::MerkleProof::virtualize(acc_c.q_roots[1], 1); if (state_root.is_null()) { @@ -397,6 +397,20 @@ HttpAnswer& HttpAnswer::operator<<(AccountCell acc_c) { } last_trans_hash = acc_info.last_trans_hash; last_trans_lt = acc_info.last_trans_lt; + block::gen::Account::Record_account acc; + block::gen::AccountStorage::Record storage_rec; + if (!tlb::unpack_cell(acc_c.root, acc)) { + abort("cannot unpack Account"); + return *this; + } + if (!tlb::csr_unpack(acc.storage, storage_rec)) { + abort("cannot unpack AccountStorage"); + return *this; + } + if (!balance.unpack(storage_rec.balance)) { + abort("cannot unpack account balance"); + return *this; + } } else if (acc_c.root.not_null()) { abort(PSTRING() << "account state proof shows that account state for " << acc_c.addr.workchain << ":" << acc_c.addr.addr.to_hex() << " must be empty, but it is not"); @@ -434,6 +448,7 @@ HttpAnswer& HttpAnswer::operator<<(AccountCell acc_c) { *this << "workchain" << acc_c.addr.workchain << ""; *this << "account hex" << acc_c.addr.addr.to_hex() << ""; *this << "account" << acc_c.addr.rserialize(true) << ""; + *this << "balance" << balance.to_str() << ""; if (last_trans_lt > 0) { *this << "last transaction" << "lt=" << last_trans_lt diff --git a/catchain/CMakeLists.txt b/catchain/CMakeLists.txt index 8ab9525da..3f766688e 100644 --- a/catchain/CMakeLists.txt +++ b/catchain/CMakeLists.txt @@ -34,6 +34,5 @@ target_include_directories(overlay PUBLIC $/.. ${OPENSSL_INCLUDE_DIR} ) -target_link_libraries(catchain PRIVATE tdutils tdactor adnl tl_api dht tdfec - overlay) +target_link_libraries(catchain PRIVATE tdutils tdactor adnl tl_api dht tdfec overlay) diff --git a/catchain/catchain-receiver.cpp b/catchain/catchain-receiver.cpp index a6ecf0611..edef90659 100644 --- a/catchain/catchain-receiver.cpp +++ b/catchain/catchain-receiver.cpp @@ -27,6 +27,8 @@ #include "catchain-receiver.hpp" +#include "td/utils/ThreadSafeCounter.h" + namespace ton { namespace catchain { @@ -369,7 +371,7 @@ void CatChainReceiverImpl::add_block(td::BufferSlice payload, std::vectorheight_ + 1; auto max_block_height = get_max_block_height(opts_, sources_.size()); - if (height > max_block_height) { + if (td::narrow_cast(height) > max_block_height) { VLOG(CATCHAIN_WARNING) << this << ": cannot create block: max height exceeded (" << max_block_height << ")"; active_send_ = false; return; @@ -685,6 +687,7 @@ void CatChainReceiverImpl::receive_query_from_overlay(adnl::AdnlNodeIdShort src, promise.set_error(td::Status::Error(ErrorCode::notready, "db not read")); return; } + TD_PERF_COUNTER(catchain_query_process); td::PerfWarningTimer t{"catchain query process", 0.05}; auto F = fetch_tl_object(data.clone(), true); if (F.is_error()) { diff --git a/common/delay.h b/common/delay.h index 9b98c58e5..3df8e7d86 100644 --- a/common/delay.h +++ b/common/delay.h @@ -49,4 +49,29 @@ template void delay_action(T promise, td::Timestamp timeout) { DelayedAction::create(std::move(promise), timeout); } + +template +class AsyncApply : public td::actor::Actor { + public: + AsyncApply(PromiseT promise, ValueT value) : promise_(std::move(promise)), value_(std::move(value)){ + } + + void start_up() override { + promise_(std::move(value_)); + stop(); + } + + static void create(td::Slice name, PromiseT promise, ValueT value ) { + td::actor::create_actor(PSLICE() << "async:" << name, std::move(promise), std::move(value)).release(); + } + + private: + PromiseT promise_; + ValueT value_; +}; + +template +void async_apply(td::Slice name, PromiseT &&promise, ValueT &&value) { + AsyncApply::create(name, std::forward(promise), std::forward(value)); +} } // namespace ton diff --git a/common/global-version.h b/common/global-version.h index a3032ebf2..533e5e8d7 100644 --- a/common/global-version.h +++ b/common/global-version.h @@ -19,6 +19,6 @@ namespace ton { // See doc/GlobalVersions.md -const int SUPPORTED_VERSION = 8; +const int SUPPORTED_VERSION = 9; } diff --git a/create-hardfork/CMakeLists.txt b/create-hardfork/CMakeLists.txt index 41b94b523..3024603c2 100644 --- a/create-hardfork/CMakeLists.txt +++ b/create-hardfork/CMakeLists.txt @@ -10,8 +10,7 @@ set(CREATE_HARDFORK_SOURCE ) add_executable(create-hardfork ${CREATE_HARDFORK_SOURCE}) -target_link_libraries(create-hardfork overlay tdutils tdactor adnl tl_api dht - rldp catchain validatorsession full-node validator-hardfork ton_validator +target_link_libraries(create-hardfork overlay tdutils tdactor adnl tl_api dht rldp catchain validatorsession full-node validator-hardfork ton_validator validator-hardfork fift-lib memprof git ${JEMALLOC_LIBRARIES}) install(TARGETS create-hardfork RUNTIME DESTINATION bin) diff --git a/create-hardfork/create-hardfork.cpp b/create-hardfork/create-hardfork.cpp index 3110b1a85..501ce3b93 100644 --- a/create-hardfork/create-hardfork.cpp +++ b/create-hardfork/create-hardfork.cpp @@ -249,7 +249,7 @@ class HardforkCreator : public td::actor::Actor { void send_block_candidate(ton::BlockIdExt block_id, ton::CatchainSeqno cc_seqno, td::uint32 validator_set_hash, td::BufferSlice data) override { } - void send_broadcast(ton::BlockBroadcast broadcast, bool custom_overlays_only) override { + void send_broadcast(ton::BlockBroadcast broadcast, int mode) override { } void download_block(ton::BlockIdExt block_id, td::uint32 priority, td::Timestamp timeout, td::Promise promise) override { diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index e21f18cbe..c2a737149 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -151,6 +151,7 @@ set(TON_DB_SOURCE vm/db/CellHashTable.h vm/db/CellStorage.h vm/db/TonDb.h + vm/db/InMemoryBagOfCellsDb.cpp ) set(FIFT_SOURCE @@ -300,9 +301,8 @@ endif() target_include_directories(ton_crypto_core SYSTEM PUBLIC $) add_library(ton_crypto STATIC ${TON_CRYPTO_SOURCE}) -target_include_directories(ton_crypto PUBLIC $ - $) -target_link_libraries(ton_crypto PUBLIC ${OPENSSL_CRYPTO_LIBRARY} tdutils tddb_utils ton_crypto_core ton_block) +target_include_directories(ton_crypto PUBLIC $ $) +target_link_libraries(ton_crypto PUBLIC ${OPENSSL_CRYPTO_LIBRARY} ton_crypto_core ton_block) if (USE_EMSCRIPTEN) target_link_options(ton_crypto PRIVATE -fexceptions) target_compile_options(ton_crypto PRIVATE -fexceptions) @@ -344,6 +344,7 @@ else() target_link_libraries(ton_crypto PUBLIC ${SECP256K1_LIBRARY}) endif() +target_link_libraries(ton_crypto_core PUBLIC ${SECP256K1_LIBRARY}) target_include_directories(ton_crypto_core PUBLIC $) target_link_libraries(ton_crypto PUBLIC ${SODIUM_LIBRARY_RELEASE}) @@ -358,7 +359,7 @@ target_link_libraries(test-ed25519-crypto PUBLIC ton_crypto) add_library(fift-lib STATIC ${FIFT_SOURCE}) target_include_directories(fift-lib PUBLIC $) -target_link_libraries(fift-lib PUBLIC ton_crypto tdutils ton_block) +target_link_libraries(fift-lib PUBLIC ton_crypto) if (USE_EMSCRIPTEN) target_link_options(fift-lib PRIVATE -fexceptions) @@ -379,7 +380,7 @@ target_link_libraries(src_parser PUBLIC ton_crypto_core) add_library(ton_block STATIC ${BLOCK_SOURCE}) target_include_directories(ton_block PUBLIC $ $ $) -target_link_libraries(ton_block PUBLIC ton_crypto tdutils tdactor tl_api) +target_link_libraries(ton_block PUBLIC ton_crypto_core tdactor tl_api) if (USE_EMSCRIPTEN) target_link_options(ton_block PRIVATE -fexceptions) target_compile_options(ton_block PRIVATE -fexceptions) @@ -387,7 +388,7 @@ endif() add_executable(func func/func-main.cpp ${FUNC_LIB_SOURCE}) target_include_directories(func PUBLIC $) -target_link_libraries(func PUBLIC ton_crypto src_parser git ton_block) +target_link_libraries(func PUBLIC ton_crypto src_parser git) if (WINGETOPT_FOUND) target_link_libraries_system(func wingetopt) endif() @@ -416,23 +417,31 @@ endif() add_executable(tlbc tl/tlbc.cpp) target_include_directories(tlbc PUBLIC $) -target_link_libraries(tlbc PUBLIC ton_crypto_core src_parser) +target_link_libraries(tlbc PUBLIC src_parser) if (WINGETOPT_FOUND) target_link_libraries_system(tlbc wingetopt) endif() add_library(pow-miner-lib util/Miner.cpp util/Miner.h) target_include_directories(pow-miner-lib PUBLIC $) -target_link_libraries(pow-miner-lib PUBLIC ton_crypto ton_block) +target_link_libraries(pow-miner-lib PUBLIC ton_crypto) add_executable(pow-miner util/pow-miner.cpp) -target_link_libraries(pow-miner PRIVATE ton_crypto ton_block pow-miner-lib git) +target_link_libraries(pow-miner PRIVATE ton_crypto pow-miner-lib git) if (WINGETOPT_FOUND) target_link_libraries_system(fift wingetopt) target_link_libraries_system(pow-miner wingetopt) endif() +add_executable(mintless-proof-generator util/mintless-proof-generator.cpp) +target_link_libraries(mintless-proof-generator PRIVATE ton_crypto git ${JEMALLOC_LIBRARIES}) + +if (JEMALLOC_FOUND) + target_include_directories(mintless-proof-generator PRIVATE ${JEMALLOC_INCLUDE_DIR}) + target_compile_definitions(mintless-proof-generator PRIVATE -DTON_USE_JEMALLOC=1) +endif() + set(TURN_OFF_LSAN cd .) if (TON_USE_ASAN AND NOT WIN32) set(TURN_OFF_LSAN export LSAN_OPTIONS=detect_leaks=0) @@ -531,12 +540,12 @@ add_executable(create-state block/create-state.cpp) target_include_directories(create-state PUBLIC $ $) if (INTERNAL_COMPILE) - target_link_libraries(create-state PUBLIC ton_crypto fift-lib ton_block tonlib git) + target_link_libraries(create-state PUBLIC ton_crypto fift-lib tonlib git) else() if (TONLIB_COMPILE) - target_link_libraries(create-state PUBLIC ton_crypto fift-lib ton_block tonlib git) + target_link_libraries(create-state PUBLIC ton_crypto fift-lib tonlib git) else() - target_link_libraries(create-state PUBLIC ton_crypto fift-lib ton_block git) + target_link_libraries(create-state PUBLIC ton_crypto fift-lib git) endif() endif() if (WINGETOPT_FOUND) @@ -546,7 +555,7 @@ endif() add_executable(dump-block block/dump-block.cpp) target_include_directories(dump-block PUBLIC $ $) -target_link_libraries(dump-block PUBLIC ton_crypto fift-lib ton_block git) +target_link_libraries(dump-block PUBLIC ton_crypto fift-lib git) if (WINGETOPT_FOUND) target_link_libraries_system(dump-block wingetopt) endif() @@ -554,7 +563,7 @@ endif() add_executable(adjust-block block/adjust-block.cpp) target_include_directories(adjust-block PUBLIC $ $) -target_link_libraries(adjust-block PUBLIC ton_crypto fift-lib ton_block git) +target_link_libraries(adjust-block PUBLIC ton_crypto fift-lib git) if (WINGETOPT_FOUND) target_link_libraries_system(dump-block wingetopt) target_link_libraries_system(adjust-block wingetopt) @@ -563,7 +572,7 @@ endif() add_executable(test-weight-distr block/test-weight-distr.cpp) target_include_directories(test-weight-distr PUBLIC $ $) -target_link_libraries(test-weight-distr PUBLIC ton_crypto fift-lib ton_block git) +target_link_libraries(test-weight-distr PUBLIC ton_crypto fift-lib git) if (WINGETOPT_FOUND) target_link_libraries_system(test-weight-distr wingetopt) endif() diff --git a/crypto/block/block.cpp b/crypto/block/block.cpp index cb371fa0b..6b9ca8bf0 100644 --- a/crypto/block/block.cpp +++ b/crypto/block/block.cpp @@ -719,8 +719,8 @@ td::uint64 BlockLimitStatus::estimate_block_size(const vm::NewCellStorageStat::S if (extra) { sum += *extra; } - return 2000 + (sum.bits >> 3) + sum.cells * 12 + sum.internal_refs * 3 + sum.external_refs * 40 + accounts * 200 + - transactions * 200 + (extra ? 200 : 0) + extra_out_msgs * 300 + public_library_diff * 700; + return 2000 + (sum.bits >> 3) + sum.cells * 12 + sum.internal_refs * 3 + sum.external_refs * 40 + transactions * 200 + + (extra ? 200 : 0) + extra_out_msgs * 300 + public_library_diff * 700; } int BlockLimitStatus::classify() const { diff --git a/crypto/block/block.h b/crypto/block/block.h index 5f3dadff4..0247d79cb 100644 --- a/crypto/block/block.h +++ b/crypto/block/block.h @@ -239,6 +239,12 @@ struct ParamLimits { bool deserialize(vm::CellSlice& cs); int classify(td::uint64 value) const; bool fits(unsigned cls, td::uint64 value) const; + void multiply_by(double x) { + CHECK(x > 0.0); + for (td::uint32& y : limits_) { + y = (td::uint32)std::min(y * x, 1e9); + } + } private: std::array limits_; diff --git a/crypto/block/block.tlb b/crypto/block/block.tlb index a3684f563..6f9754267 100644 --- a/crypto/block/block.tlb +++ b/crypto/block/block.tlb @@ -666,15 +666,15 @@ wc_split_merge_timings#0 //workchain#a5 enabled_since:uint32 min_split:(## 8) max_split:(## 8) // { min_split <= max_split } { max_split <= 60 } -workchain#a6 enabled_since:uint32 actual_min_split:(## 8) - min_split:(## 8) max_split:(## 8) { actual_min_split <= min_split } +workchain#a6 enabled_since:uint32 monitor_min_split:(## 8) + min_split:(## 8) max_split:(## 8) { monitor_min_split <= min_split } basic:(## 1) active:Bool accept_msgs:Bool flags:(## 13) { flags = 0 } zerostate_root_hash:bits256 zerostate_file_hash:bits256 version:uint32 format:(WorkchainFormat basic) = WorkchainDescr; -workchain_v2#a7 enabled_since:uint32 actual_min_split:(## 8) - min_split:(## 8) max_split:(## 8) { actual_min_split <= min_split } +workchain_v2#a7 enabled_since:uint32 monitor_min_split:(## 8) + min_split:(## 8) max_split:(## 8) { monitor_min_split <= min_split } basic:(## 1) active:Bool accept_msgs:Bool flags:(## 13) { flags = 0 } zerostate_root_hash:bits256 zerostate_file_hash:bits256 version:uint32 format:(WorkchainFormat basic) diff --git a/crypto/block/create-state.cpp b/crypto/block/create-state.cpp index 348377e94..c8c8b970d 100644 --- a/crypto/block/create-state.cpp +++ b/crypto/block/create-state.cpp @@ -426,7 +426,7 @@ bool store_validator_list_hash(vm::CellBuilder& cb) { LOG_CHECK(vset) << "unpacked validator set is empty"; auto ccvc = block::Config::unpack_catchain_validators_config(config_dict.lookup_ref(td::BitArray<32>{28})); ton::ShardIdFull shard{ton::masterchainId}; - auto nodes = block::Config::do_compute_validator_set(ccvc, shard, *vset, now, 0); + auto nodes = block::Config::do_compute_validator_set(ccvc, shard, *vset, 0); LOG_CHECK(!nodes.empty()) << "validator node list in unpacked validator set is empty"; auto vset_hash = block::compute_validator_set_hash(0, shard, std::move(nodes)); LOG(DEBUG) << "initial validator set hash is " << vset_hash; diff --git a/crypto/block/mc-config.cpp b/crypto/block/mc-config.cpp index 6da0f034d..56ee85ae3 100644 --- a/crypto/block/mc-config.cpp +++ b/crypto/block/mc-config.cpp @@ -320,7 +320,7 @@ ton::ValidatorSessionConfig Config::get_consensus_config() const { c.max_block_size = r.max_block_bytes; c.max_collated_data_size = r.max_collated_bytes; }; - auto set_v2 = [&] (auto& r) { + auto set_v2 = [&](auto& r) { set_v1(r); c.new_catchain_ids = r.new_catchain_ids; }; @@ -1746,7 +1746,7 @@ ton::CatchainSeqno ConfigInfo::get_shard_cc_seqno(ton::ShardIdFull shard) const std::vector Config::compute_validator_set(ton::ShardIdFull shard, const block::ValidatorSet& vset, ton::UnixTime time, ton::CatchainSeqno cc_seqno) const { - return do_compute_validator_set(get_catchain_validators_config(), shard, vset, time, cc_seqno); + return do_compute_validator_set(get_catchain_validators_config(), shard, vset, cc_seqno); } std::vector Config::compute_validator_set(ton::ShardIdFull shard, ton::UnixTime time, @@ -1773,7 +1773,7 @@ std::vector ConfigInfo::compute_validator_set_cc(ton::Shard if (cc_seqno_delta) { cc_seqno = *cc_seqno_delta += cc_seqno; } - return do_compute_validator_set(get_catchain_validators_config(), shard, vset, time, cc_seqno); + return do_compute_validator_set(get_catchain_validators_config(), shard, vset, cc_seqno); } std::vector ConfigInfo::compute_validator_set_cc(ton::ShardIdFull shard, ton::UnixTime time, @@ -1856,9 +1856,8 @@ int ValidatorSet::lookup_public_key(td::ConstBitPtr pubkey) const { return -1; } -std::vector Config::do_compute_validator_set(const block::CatchainValidatorsConfig& ccv_conf, - ton::ShardIdFull shard, - const block::ValidatorSet& vset, ton::UnixTime time, +std::vector Config::do_compute_validator_set(const CatchainValidatorsConfig& ccv_conf, + ton::ShardIdFull shard, const ValidatorSet& vset, ton::CatchainSeqno cc_seqno) { // LOG(DEBUG) << "in Config::do_compute_validator_set() for " << shard.to_str() << " ; cc_seqno=" << cc_seqno; std::vector nodes; @@ -1940,7 +1939,7 @@ td::Result Config::get_size_limits_config() const { td::Result Config::do_get_size_limits_config(td::Ref cs) { SizeLimitsConfig limits; if (cs.is_null()) { - return limits; // default values + return limits; // default values } auto unpack_v1 = [&](auto& rec) { limits.max_msg_bits = rec.max_msg_bits; @@ -2075,7 +2074,7 @@ bool WorkchainInfo::unpack(ton::WorkchainId wc, vm::CellSlice& cs) { } auto unpack_v1 = [this](auto& info) { enabled_since = info.enabled_since; - actual_min_split = info.actual_min_split; + monitor_min_split = info.monitor_min_split; min_split = info.min_split; max_split = info.max_split; basic = info.basic; @@ -2299,17 +2298,14 @@ td::Result> ConfigInfo::get_prev_blocks_info() const { if (shard->sgn() < 0) { shard &= ((td::make_refint(1) << 64) - 1); } - return vm::make_tuple_ref( - td::make_refint(block_id.id.workchain), - std::move(shard), - td::make_refint(block_id.id.seqno), - td::bits_to_refint(block_id.root_hash.bits(), 256), - td::bits_to_refint(block_id.file_hash.bits(), 256)); + return vm::make_tuple_ref(td::make_refint(block_id.id.workchain), std::move(shard), + td::make_refint(block_id.id.seqno), td::bits_to_refint(block_id.root_hash.bits(), 256), + td::bits_to_refint(block_id.file_hash.bits(), 256)); }; std::vector last_mc_blocks; last_mc_blocks.push_back(block_id_to_tuple(block_id)); - for (ton::BlockSeqno seqno = block_id.id.seqno; seqno > 0 && last_mc_blocks.size() < 16; ) { + for (ton::BlockSeqno seqno = block_id.id.seqno; seqno > 0 && last_mc_blocks.size() < 16;) { --seqno; ton::BlockIdExt block_id; if (!get_old_mc_block_id(seqno, block_id)) { @@ -2323,9 +2319,8 @@ td::Result> ConfigInfo::get_prev_blocks_info() const { if (!get_last_key_block(last_key_block, last_key_block_lt)) { return td::Status::Error("cannot fetch last key block"); } - return vm::make_tuple_ref( - td::make_cnt_ref>(std::move(last_mc_blocks)), - block_id_to_tuple(last_key_block)); + return vm::make_tuple_ref(td::make_cnt_ref>(std::move(last_mc_blocks)), + block_id_to_tuple(last_key_block)); } td::optional PrecompiledContractsConfig::get_contract( diff --git a/crypto/block/mc-config.h b/crypto/block/mc-config.h index 25d94ec7e..7cb6613d7 100644 --- a/crypto/block/mc-config.h +++ b/crypto/block/mc-config.h @@ -197,6 +197,7 @@ struct McShardHash : public McShardHashI { : blk_(blk), start_lt_(start_lt), end_lt_(end_lt) { } McShardHash(const McShardHash&) = default; + McShardHash& operator=(const McShardHash&) = default; bool is_valid() const { return blk_.is_valid(); } @@ -414,7 +415,7 @@ struct CatchainValidatorsConfig { struct WorkchainInfo : public td::CntObject { ton::WorkchainId workchain{ton::workchainInvalid}; ton::UnixTime enabled_since; - td::uint32 actual_min_split; + td::uint32 monitor_min_split; td::uint32 min_split, max_split; bool basic; bool active; @@ -455,10 +456,11 @@ class ShardConfig { ShardConfig() = default; ShardConfig(const ShardConfig& other); ShardConfig(ShardConfig&& other) = default; - ShardConfig(Ref shard_hashes, Ref mc_shard_hash = {}) + explicit ShardConfig(Ref shard_hashes, Ref mc_shard_hash = {}) : shard_hashes_(std::move(shard_hashes)), mc_shard_hash_(std::move(mc_shard_hash)) { init(); } + ShardConfig& operator=(ShardConfig&& other) = default; bool is_valid() const { return valid_; } @@ -545,7 +547,10 @@ class Config { }; public: - enum { needValidatorSet = 16, needSpecialSmc = 32, needWorkchainInfo = 256, needCapabilities = 512 }; + static constexpr int needValidatorSet = 16; + static constexpr int needSpecialSmc = 32; + static constexpr int needWorkchainInfo = 256; + static constexpr int needCapabilities = 512; int mode{0}; ton::BlockIdExt block_id; @@ -655,9 +660,8 @@ class Config { BurningConfig get_burning_config() const; td::Ref get_unpacked_config_tuple(ton::UnixTime now) const; PrecompiledContractsConfig get_precompiled_contracts_config() const; - static std::vector do_compute_validator_set(const block::CatchainValidatorsConfig& ccv_conf, - ton::ShardIdFull shard, - const block::ValidatorSet& vset, ton::UnixTime time, + static std::vector do_compute_validator_set(const CatchainValidatorsConfig& ccv_conf, + ton::ShardIdFull shard, const ValidatorSet& vset, ton::CatchainSeqno cc_seqno); static td::Result> unpack_config(Ref config_root, @@ -682,14 +686,12 @@ class Config { class ConfigInfo : public Config, public ShardConfig { public: - enum { - needStateRoot = 1, - needLibraries = 2, - needStateExtraRoot = 4, - needShardHashes = 8, - needAccountsRoot = 64, - needPrevBlocks = 128 - }; + static constexpr int needStateRoot = 1; + static constexpr int needLibraries = 2; + static constexpr int needStateExtraRoot = 4; + static constexpr int needShardHashes = 8; + static constexpr int needAccountsRoot = 64; + static constexpr int needPrevBlocks = 128; ton::BlockSeqno vert_seqno{~0U}; int global_id_{0}; ton::UnixTime utime{0}; diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index dbf0199e7..a32bad52f 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -1555,7 +1555,14 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) { // ... compute_phase = std::make_unique(); ComputePhase& cp = *(compute_phase.get()); - original_balance -= total_fees; + if (cfg.global_version >= 9) { + original_balance = balance; + if (msg_balance_remaining.is_valid()) { + original_balance -= msg_balance_remaining; + } + } else { + original_balance -= total_fees; + } if (td::sgn(balance.grams) <= 0) { // no gas cp.skip_reason = ComputePhase::sk_no_gas; @@ -2860,22 +2867,26 @@ td::Status Transaction::check_state_limits(const SizeLimitsConfig& size_limits, vm::CellStorageStat storage_stat; storage_stat.limit_cells = size_limits.max_acc_state_cells; storage_stat.limit_bits = size_limits.max_acc_state_bits; - td::Timer timer; - auto add_used_storage = [&](const td::Ref& cell) -> td::Status { - if (cell.not_null()) { - TRY_RESULT(res, storage_stat.add_used_storage(cell)); - if (res.max_merkle_depth > max_allowed_merkle_depth) { - return td::Status::Error("too big merkle depth"); + { + TD_PERF_COUNTER(transaction_storage_stat_a); + td::Timer timer; + auto add_used_storage = [&](const td::Ref& cell) -> td::Status { + if (cell.not_null()) { + TRY_RESULT(res, storage_stat.add_used_storage(cell)); + if (res.max_merkle_depth > max_allowed_merkle_depth) { + return td::Status::Error("too big merkle depth"); + } } + return td::Status::OK(); + }; + TRY_STATUS(add_used_storage(new_code)); + TRY_STATUS(add_used_storage(new_data)); + TRY_STATUS(add_used_storage(new_library)); + if (timer.elapsed() > 0.1) { + LOG(INFO) << "Compute used storage took " << timer.elapsed() << "s"; } - return td::Status::OK(); - }; - TRY_STATUS(add_used_storage(new_code)); - TRY_STATUS(add_used_storage(new_data)); - TRY_STATUS(add_used_storage(new_library)); - if (timer.elapsed() > 0.1) { - LOG(INFO) << "Compute used storage took " << timer.elapsed() << "s"; } + if (acc_status == Account::acc_active) { storage_stat.clear_limit(); } else { @@ -3156,6 +3167,7 @@ bool Transaction::compute_state() { if (new_stats) { stats = new_stats.unwrap(); } else { + TD_PERF_COUNTER(transaction_storage_stat_b); td::Timer timer; stats.add_used_storage(Ref(storage)).ensure(); if (timer.elapsed() > 0.1) { diff --git a/crypto/common/bigint.hpp b/crypto/common/bigint.hpp index f8756d9f6..4061f82d3 100644 --- a/crypto/common/bigint.hpp +++ b/crypto/common/bigint.hpp @@ -2294,11 +2294,11 @@ std::string AnyIntView::to_dec_string_destroy_any() { stack.push_back(divmod_short_any(Tr::max_pow10)); } while (sgn()); char slice[word_bits * 97879 / 325147 + 2]; - std::sprintf(slice, "%lld", stack.back()); + std::snprintf(slice, sizeof(slice), "%lld", stack.back()); s += slice; stack.pop_back(); while (stack.size()) { - std::sprintf(slice, "%018lld", stack.back()); + std::snprintf(slice, sizeof(slice), "%018lld", stack.back()); s += slice; stack.pop_back(); } diff --git a/crypto/common/refcnt.cpp b/crypto/common/refcnt.cpp index 7ef0857cc..c6d1b8884 100644 --- a/crypto/common/refcnt.cpp +++ b/crypto/common/refcnt.cpp @@ -29,6 +29,7 @@ Ref CntObject::clone() const { namespace detail { struct SafeDeleter { public: + thread_local static td::int64 delete_count; void retire(const CntObject *ptr) { if (is_active_) { to_delete_.push_back(ptr); @@ -39,9 +40,11 @@ struct SafeDeleter { is_active_ = false; }; delete ptr; + delete_count++; while (!to_delete_.empty()) { auto *ptr = to_delete_.back(); to_delete_.pop_back(); + delete_count++; delete ptr; } } @@ -50,6 +53,7 @@ struct SafeDeleter { std::vector to_delete_; bool is_active_{false}; }; +thread_local td::int64 SafeDeleter::delete_count{0}; TD_THREAD_LOCAL SafeDeleter *deleter; void safe_delete(const CntObject *ptr) { @@ -57,4 +61,7 @@ void safe_delete(const CntObject *ptr) { deleter->retire(ptr); } } // namespace detail +int64 ref_get_delete_count() { + return detail::SafeDeleter::delete_count; +} } // namespace td diff --git a/crypto/common/refcnt.hpp b/crypto/common/refcnt.hpp index ef50c3b9a..953cc7797 100644 --- a/crypto/common/refcnt.hpp +++ b/crypto/common/refcnt.hpp @@ -472,5 +472,6 @@ template void swap(Ref& r1, Ref& r2) { r1.swap(r2); } +int64 ref_get_delete_count(); } // namespace td diff --git a/crypto/fift/words.cpp b/crypto/fift/words.cpp index 324f492c0..cbe1dbcad 100644 --- a/crypto/fift/words.cpp +++ b/crypto/fift/words.cpp @@ -3425,7 +3425,7 @@ void import_cmdline_args(Dictionary& d, std::string arg0, int n, const char* con cmdline_args->set(std::move(list)); for (int i = 1; i <= n; i++) { char buffer[14]; - sprintf(buffer, "$%d ", i); + snprintf(buffer, sizeof(buffer), "$%d ", i); d.def_stack_word(buffer, std::bind(interpret_get_fixed_cmdline_arg, _1, i)); } } diff --git a/crypto/func/analyzer.cpp b/crypto/func/analyzer.cpp index fb05bbb4b..9d59cf9ad 100644 --- a/crypto/func/analyzer.cpp +++ b/crypto/func/analyzer.cpp @@ -81,7 +81,7 @@ bool CodeBlob::compute_used_code_vars(std::unique_ptr& ops_ptr, const VarDes func_assert(ops_ptr->cl == Op::_Nop); return ops_ptr->set_var_info(var_info); } - return compute_used_code_vars(ops_ptr->next, var_info, edit) | ops_ptr->compute_used_vars(*this, edit); + return int(compute_used_code_vars(ops_ptr->next, var_info, edit)) | int(ops_ptr->compute_used_vars(*this, edit)); } bool operator==(const VarDescrList& x, const VarDescrList& y) { @@ -584,7 +584,7 @@ bool prune_unreachable(std::unique_ptr& ops) { ops = std::move(op.block1); return prune_unreachable(ops); } else { - reach = prune_unreachable(op.block0) | prune_unreachable(op.block1); + reach = int(prune_unreachable(op.block0)) | int(prune_unreachable(op.block1)); } break; } @@ -660,7 +660,7 @@ bool prune_unreachable(std::unique_ptr& ops) { break; } case Op::_TryCatch: { - reach = prune_unreachable(op.block0) | prune_unreachable(op.block1); + reach = int(prune_unreachable(op.block0)) | int(prune_unreachable(op.block1)); break; } default: @@ -892,15 +892,15 @@ bool Op::mark_noreturn() { return set_noreturn(true); case _If: case _TryCatch: - return set_noreturn((block0->mark_noreturn() & (block1 && block1->mark_noreturn())) | next->mark_noreturn()); + return set_noreturn((int(block0->mark_noreturn()) & int(block1 && block1->mark_noreturn())) | int(next->mark_noreturn())); case _Again: block0->mark_noreturn(); return set_noreturn(true); case _Until: - return set_noreturn(block0->mark_noreturn() | next->mark_noreturn()); + return set_noreturn(int(block0->mark_noreturn()) | int(next->mark_noreturn())); case _While: block1->mark_noreturn(); - return set_noreturn(block0->mark_noreturn() | next->mark_noreturn()); + return set_noreturn(int(block0->mark_noreturn()) | int(next->mark_noreturn())); case _Repeat: block0->mark_noreturn(); return set_noreturn(next->mark_noreturn()); diff --git a/crypto/func/auto-tests/tests/bit-operators.fc b/crypto/func/auto-tests/tests/bit-operators.fc new file mode 100644 index 000000000..3cc5b5226 --- /dev/null +++ b/crypto/func/auto-tests/tests/bit-operators.fc @@ -0,0 +1,53 @@ + +int lshift() { + return (1 << 0) == 1; +} + +int rshift() { + return (1 >> 0) == 1; +} + +int lshift_var(int i) { + return (1 << i) == 1; +} + +int rshift_var(int i) { + return (1 >> i) == 1; +} + +int main(int x) { + if (x == 0) { + return lshift(); + } elseif (x == 1) { + return rshift(); + } elseif (x == 2) { + return lshift_var(0); + } elseif (x == 3) { + return rshift_var(0); + } elseif (x == 4) { + return lshift_var(1); + } else { + return rshift_var(1); + } +} + +int is_claimed(int index) method_id(11) { + int claim_bit_index = index % 256; + int mask = 1 << claim_bit_index; + return (255 & mask) == mask; +} + + +{- + method_id | in | out +TESTCASE | 0 | 0 | -1 +TESTCASE | 0 | 1 | -1 +TESTCASE | 0 | 2 | -1 +TESTCASE | 0 | 3 | -1 +TESTCASE | 0 | 4 | 0 +TESTCASE | 0 | 5 | 0 +TESTCASE | 11 | 0 | -1 +TESTCASE | 11 | 1 | -1 +TESTCASE | 11 | 256 | -1 +TESTCASE | 11 | 8 | 0 +-} diff --git a/crypto/func/builtins.cpp b/crypto/func/builtins.cpp index 9de673fd7..cf3adf418 100644 --- a/crypto/func/builtins.cpp +++ b/crypto/func/builtins.cpp @@ -264,7 +264,7 @@ int emulate_lshift(int a, int b) { } int t = ((b & VarDescr::_NonZero) ? VarDescr::_Even : 0); t |= b & VarDescr::_Finite; - return emulate_mul(a, VarDescr::_Int | VarDescr::_Pos | VarDescr::_NonZero | VarDescr::_Even | t); + return emulate_mul(a, VarDescr::_Int | VarDescr::_Pos | VarDescr::_NonZero | t); } int emulate_div(int a, int b) { @@ -310,7 +310,7 @@ int emulate_rshift(int a, int b) { } int t = ((b & VarDescr::_NonZero) ? VarDescr::_Even : 0); t |= b & VarDescr::_Finite; - return emulate_div(a, VarDescr::_Int | VarDescr::_Pos | VarDescr::_NonZero | VarDescr::_Even | t); + return emulate_div(a, VarDescr::_Int | VarDescr::_Pos | VarDescr::_NonZero | t); } int emulate_mod(int a, int b, int round_mode = -1) { diff --git a/crypto/func/func.h b/crypto/func/func.h index 2b95bcbc5..e2476911d 100644 --- a/crypto/func/func.h +++ b/crypto/func/func.h @@ -45,7 +45,7 @@ extern std::string generated_from; constexpr int optimize_depth = 20; -const std::string func_version{"0.4.4"}; +const std::string func_version{"0.4.5"}; enum Keyword { _Eof = -1, diff --git a/crypto/openssl/digest.hpp b/crypto/openssl/digest.hpp index 5c232df99..2adeef1d7 100644 --- a/crypto/openssl/digest.hpp +++ b/crypto/openssl/digest.hpp @@ -48,6 +48,7 @@ struct OpensslEVP_SHA512 { template class HashCtx { + EVP_MD_CTX *base_ctx{nullptr}; EVP_MD_CTX *ctx{nullptr}; void init(); void clear(); @@ -77,16 +78,20 @@ class HashCtx { template void HashCtx::init() { ctx = EVP_MD_CTX_create(); + base_ctx = EVP_MD_CTX_create(); + EVP_DigestInit_ex(base_ctx, H::get_evp(), 0); reset(); } template void HashCtx::reset() { - EVP_DigestInit_ex(ctx, H::get_evp(), 0); + EVP_MD_CTX_copy_ex(ctx, base_ctx); } template void HashCtx::clear() { + EVP_MD_CTX_destroy(base_ctx); + base_ctx = nullptr; EVP_MD_CTX_destroy(ctx); ctx = nullptr; } diff --git a/crypto/smartcont/wallet-v3.fif b/crypto/smartcont/wallet-v3.fif index c090dc095..f44e0c921 100644 --- a/crypto/smartcont/wallet-v3.fif +++ b/crypto/smartcont/wallet-v3.fif @@ -13,7 +13,7 @@ variable extra-currencies { extra-currencies @ cc+ extra-currencies ! } : extra-cc+! begin-options - " [-x *] [-n|-b] [-t] [-B ] [-C ] []" +cr +tab + " [-x *] [-n|-b] [-t] [-B ] [-C ] [-I ] []" +cr +tab +"Creates a request to advanced wallet created by new-wallet-v3.fif, with private key loaded from file .pk " +"and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" disable-digit-options generic-help-setopt @@ -29,6 +29,8 @@ begin-options "Sets the payload of the transfer message" option-help "C" "--comment" { =: comment } short-long-option-arg "Sets the comment to be sent in the transfer message" option-help + "I" "--with-init" { =: init-file } short-long-option-arg + "Indicates filename with BoC containing StateInit for internal message" option-help "m" "--mode" { parse-int =: send-mode } short-long-option-arg "Sets transfer mode (0..255) for SENDRAWMSG (" send-mode (.) $+ +" by default)" option-help @@ -57,14 +59,18 @@ file-base +".pk" load-keypair nip constant wallet_pk def? body-boc-file { @' body-boc-file file>B B>boc } { comment simple-transfer-body } cond constant body-cell +def? init-file { @' init-file file>B B>boc diff --git a/crypto/smc-envelope/GenericAccount.cpp b/crypto/smc-envelope/GenericAccount.cpp index 04249699c..4c9bd1659 100644 --- a/crypto/smc-envelope/GenericAccount.cpp +++ b/crypto/smc-envelope/GenericAccount.cpp @@ -61,7 +61,8 @@ block::StdAddress GenericAccount::get_address(ton::WorkchainId workchain_id, return block::StdAddress(workchain_id, init_state->get_hash().bits(), true /*bounce*/); } -void GenericAccount::store_int_message(vm::CellBuilder& cb, const block::StdAddress& dest_address, td::int64 gramms) { +void GenericAccount::store_int_message(vm::CellBuilder& cb, const block::StdAddress& dest_address, td::int64 gramms, + td::Ref extra_currencies) { td::BigInt256 dest_addr; dest_addr.import_bits(dest_address.addr.as_bitslice()); cb.store_zeroes(1) @@ -73,7 +74,8 @@ void GenericAccount::store_int_message(vm::CellBuilder& cb, const block::StdAddr .store_long(dest_address.workchain, 8) .store_int256(dest_addr, 256); block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms)); - cb.store_zeroes(9 + 64 + 32); + cb.store_maybe_ref(extra_currencies); + cb.store_zeroes(8 + 64 + 32); } td::Ref GenericAccount::create_ext_message(const block::StdAddress& address, td::Ref new_state, diff --git a/crypto/smc-envelope/GenericAccount.h b/crypto/smc-envelope/GenericAccount.h index 285553c78..93a059f99 100644 --- a/crypto/smc-envelope/GenericAccount.h +++ b/crypto/smc-envelope/GenericAccount.h @@ -36,7 +36,8 @@ class GenericAccount { static block::StdAddress get_address(ton::WorkchainId workchain_id, const td::Ref& init_state) noexcept; static td::Ref create_ext_message(const block::StdAddress& address, td::Ref new_state, td::Ref body) noexcept; - static void store_int_message(vm::CellBuilder& cb, const block::StdAddress& dest_address, td::int64 gramms); + static void store_int_message(vm::CellBuilder& cb, const block::StdAddress& dest_address, td::int64 gramms, + td::Ref extra_currencies); static td::Result get_public_key(const SmartContract& sc); static td::Result get_seqno(const SmartContract& sc); diff --git a/crypto/smc-envelope/WalletInterface.cpp b/crypto/smc-envelope/WalletInterface.cpp index e02759b23..eecec4354 100644 --- a/crypto/smc-envelope/WalletInterface.cpp +++ b/crypto/smc-envelope/WalletInterface.cpp @@ -48,7 +48,7 @@ td::Result> WalletInterface::get_init_message(const td::Ed2551 td::Ref WalletInterface::create_int_message(const Gift &gift) { vm::CellBuilder cbi; - GenericAccount::store_int_message(cbi, gift.destination, gift.gramms < 0 ? 0 : gift.gramms); + GenericAccount::store_int_message(cbi, gift.destination, gift.gramms < 0 ? 0 : gift.gramms, gift.extra_currencies); if (gift.init_state.not_null()) { cbi.store_ones(2); cbi.store_ref(gift.init_state); diff --git a/crypto/smc-envelope/WalletInterface.h b/crypto/smc-envelope/WalletInterface.h index c4e1f270e..e88ca0a61 100644 --- a/crypto/smc-envelope/WalletInterface.h +++ b/crypto/smc-envelope/WalletInterface.h @@ -37,6 +37,7 @@ class WalletInterface : public SmartContract { struct Gift { block::StdAddress destination; td::int64 gramms; + td::Ref extra_currencies; td::int32 send_mode{-1}; bool is_encrypted{false}; diff --git a/crypto/test/Ed25519.cpp b/crypto/test/Ed25519.cpp index 45e8891f4..131bfe92e 100644 --- a/crypto/test/Ed25519.cpp +++ b/crypto/test/Ed25519.cpp @@ -17,6 +17,8 @@ Copyright 2017-2020 Telegram Systems LLP */ #include "crypto/Ed25519.h" +#include "ellcurve/Ed25519.h" + #include "td/utils/logging.h" #include "td/utils/misc.h" #include "td/utils/Slice.h" @@ -24,6 +26,8 @@ #include "td/utils/JsonBuilder.h" #include "wycheproof.h" +#include "keys/keys.hpp" +#include "td/utils/benchmark.h" #include #include @@ -217,3 +221,36 @@ TEST(Crypto, almost_zero) { } } } + +BENCH(ed25519_sign, "ed25519_sign") { + auto private_key = td::Ed25519::generate_private_key().move_as_ok(); + std::string hash_to_sign(32, 'a'); + for (int i = 0; i < n; i++) { + private_key.sign(hash_to_sign).ensure(); + } +} + +BENCH(ed25519_shared_secret, "ed25519_shared_secret") { + auto private_key_a = td::Ed25519::generate_private_key().move_as_ok(); + auto private_key_b = td::Ed25519::generate_private_key().move_as_ok(); + auto public_key_b = private_key_a.get_public_key().move_as_ok(); + for (int i = 0; i < n; i++) { + td::Ed25519::compute_shared_secret(public_key_b, private_key_a).ensure(); + } +} + +BENCH(ed25519_verify, "ed25519_verify") { + auto private_key = td::Ed25519::generate_private_key().move_as_ok(); + std::string hash_to_sign(32, 'a'); + auto public_key = private_key.get_public_key().move_as_ok(); + auto signature = private_key.sign(hash_to_sign).move_as_ok(); + for (int i = 0; i < n; i++) { + public_key.verify_signature(hash_to_sign, signature).ensure(); + } +} + +TEST(Crypto, ed25519_benchmark) { + bench(ed25519_signBench()); + bench(ed25519_shared_secretBench()); + bench(ed25519_verifyBench()); +} \ No newline at end of file diff --git a/crypto/test/test-db.cpp b/crypto/test/test-db.cpp index 35727ee36..dc7fcf370 100644 --- a/crypto/test/test-db.cpp +++ b/crypto/test/test-db.cpp @@ -54,12 +54,88 @@ #include #include +#include #include #include "openssl/digest.hpp" +#include "vm/dict.h" + +#include +#include +#include +#include +#include namespace vm { +class ThreadExecutor : public DynamicBagOfCellsDb::AsyncExecutor { + public: + explicit ThreadExecutor(size_t threads_n) { + for (size_t i = 0; i < threads_n; ++i) { + threads_.emplace_back([this]() { + while (true) { + auto task = pop_task(); + if (!task) { + break; + } + CHECK(generation_.load() % 2 == 1); + task(); + } + }); + } + } + + ~ThreadExecutor() override { + for (size_t i = 0; i < threads_.size(); ++i) { + push_task({}); + } + for (auto &t : threads_) { + t.join(); + } + } + + void execute_async(std::function f) override { + push_task(std::move(f)); + } + + void execute_sync(std::function f) override { + auto x = generation_.load(); + std::scoped_lock lock(sync_mutex_); + CHECK(x == generation_); + CHECK(generation_.load() % 2 == 1); + f(); + CHECK(generation_.load() % 2 == 1); + } + void inc_generation() { + generation_.fetch_add(1); + } + + private: + std::atomic generation_{0}; + std::queue, size_t>> queue_; + std::mutex queue_mutex_; + std::condition_variable cv_; + std::mutex sync_mutex_; + std::vector threads_; + + std::function pop_task() { + std::unique_lock lock(queue_mutex_); + cv_.wait(lock, [&] { return !queue_.empty(); }); + CHECK(!queue_.empty()); + auto task = std::move(queue_.front()); + queue_.pop(); + CHECK(task.second == generation_); + return task.first; + } + + void push_task(std::function task) { + { + std::scoped_lock lock(queue_mutex_); + queue_.emplace(std::move(task), generation_.load()); + } + cv_.notify_one(); + } +}; std::vector do_get_serialization_modes() { std::vector res; @@ -82,9 +158,23 @@ int get_random_serialization_mode(T &rnd) { return modes[rnd.fast(0, (int)modes.size() - 1)]; } -class BenchSha256 : public td::Benchmark { +class BenchSha : public td::Benchmark { public: + explicit BenchSha(size_t n) : str_(n, 'a') { + } std::string get_description() const override { + return PSTRING() << get_name() << " length=" << str_.size(); + } + + virtual std::string get_name() const = 0; + + protected: + std::string str_; +}; +class BenchSha256 : public BenchSha { + public: + using BenchSha::BenchSha; + std::string get_name() const override { return "SHA256"; } @@ -92,7 +182,7 @@ class BenchSha256 : public td::Benchmark { int res = 0; for (int i = 0; i < n; i++) { digest::SHA256 hasher; - hasher.feed("abcd", 4); + hasher.feed(str_); unsigned char buf[32]; hasher.extract(buf); res += buf[0]; @@ -100,10 +190,12 @@ class BenchSha256 : public td::Benchmark { td::do_not_optimize_away(res); } }; -class BenchSha256Reuse : public td::Benchmark { +class BenchSha256Reuse : public BenchSha { public: - std::string get_description() const override { - return "SHA256 reuse"; + using BenchSha::BenchSha; + + std::string get_name() const override { + return "SHA256 reuse (used in DataCell)"; } void run(int n) override { @@ -111,7 +203,7 @@ class BenchSha256Reuse : public td::Benchmark { digest::SHA256 hasher; for (int i = 0; i < n; i++) { hasher.reset(); - hasher.feed("abcd", 4); + hasher.feed(str_); unsigned char buf[32]; hasher.extract(buf); res += buf[0]; @@ -119,28 +211,46 @@ class BenchSha256Reuse : public td::Benchmark { td::do_not_optimize_away(res); } }; -class BenchSha256Low : public td::Benchmark { +class BenchSha256Low : public BenchSha { public: - std::string get_description() const override { + using BenchSha::BenchSha; + + std::string get_name() const override { return "SHA256 low level"; } +// Use the old method to check for performance degradation +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) // Disable deprecated warning for MSVC +#endif void run(int n) override { int res = 0; - td::Sha256State ctx; + SHA256_CTX ctx; for (int i = 0; i < n; i++) { - ctx.init(); - ctx.feed("abcd"); + SHA256_Init(&ctx); + SHA256_Update(&ctx, str_.data(), str_.size()); unsigned char buf[32]; - ctx.extract(td::MutableSlice{buf, 32}); + SHA256_Final(buf, &ctx); res += buf[0]; } td::do_not_optimize_away(res); } }; -class BenchSha256Tdlib : public td::Benchmark { +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif + +class BenchSha256Tdlib : public BenchSha { public: - std::string get_description() const override { + using BenchSha::BenchSha; + + std::string get_name() const override { return "SHA256 TDLib"; } @@ -150,7 +260,7 @@ class BenchSha256Tdlib : public td::Benchmark { for (int i = 0; i < n; i++) { td::init_thread_local(ctx); ctx->init(); - ctx->feed("abcd"); + ctx->feed(str_); unsigned char buf[32]; ctx->extract(td::MutableSlice(buf, 32), false); res += buf[0]; @@ -158,11 +268,61 @@ class BenchSha256Tdlib : public td::Benchmark { td::do_not_optimize_away(res); } }; + +template +void bench_threaded(F &&f) { + class Threaded : public td::Benchmark { + public: + explicit Threaded(F &&f) : f_(std::move(f)), base(f_()) { + } + F f_; + std::decay_t base; + + std::string get_description() const override { + return base.get_description() + " threaded"; + } + + void run(int n) override { + std::atomic task_i{0}; + int chunk_size = 1024; + int num_threads = 16; + n *= num_threads; + std::vector threads; + for (int i = 0; i < num_threads; i++) { + threads.emplace_back([&]() mutable { + auto bench = f_(); + while (true) { + i = task_i.fetch_add(chunk_size, std::memory_order_relaxed); + auto i_end = std::min(n, i + chunk_size); + if (i > n) { + break; + } + bench.run(i_end - i); + } + }); + } + for (auto &thread : threads) { + thread.join(); + } + }; + }; + bench(Threaded(std::forward(f))); +} TEST(Cell, sha_benchmark) { - bench(BenchSha256Tdlib()); - bench(BenchSha256Low()); - bench(BenchSha256Reuse()); - bench(BenchSha256()); + for (size_t n : {4, 64, 128}) { + bench(BenchSha256Tdlib(n)); + bench(BenchSha256Low(n)); + bench(BenchSha256Reuse(n)); + bench(BenchSha256(n)); + } +} +TEST(Cell, sha_benchmark_threaded) { + for (size_t n : {4, 64, 128}) { + bench_threaded([n] { return BenchSha256Tdlib(n); }); + bench_threaded([n]() { return BenchSha256Low(n); }); + bench_threaded([n]() { return BenchSha256Reuse(n); }); + bench_threaded([n]() { return BenchSha256(n); }); + } } std::string serialize_boc(Ref cell, int mode = 31) { @@ -762,16 +922,136 @@ TEST(TonDb, BocMultipleRoots) { } }; -TEST(TonDb, DynamicBoc) { +TEST(TonDb, InMemoryDynamicBocSimple) { + auto counter = [] { return td::NamedThreadSafeCounter::get_default().get_counter("DataCell").sum(); }; + auto before = counter(); + SCOPE_EXIT { + LOG_CHECK(before == counter()) << before << " vs " << counter(); + ; + }; td::Random::Xorshift128plus rnd{123}; + auto kv = std::make_shared(); + CellStorer storer(*kv); + + auto boc = DynamicBagOfCellsDb::create_in_memory(kv.get(), {}); + + auto empty_cell = vm::CellBuilder().finalize(); + boc->inc(empty_cell); + boc->prepare_commit().ensure(); + boc->commit(storer).ensure(); + auto got_empty_cell = boc->load_cell(empty_cell->get_hash().as_slice()).move_as_ok(); + ASSERT_EQ(empty_cell->get_hash(), got_empty_cell->get_hash()); + + boc->dec(empty_cell); + + auto one_ref_cell = vm::CellBuilder().store_ref(empty_cell).finalize(); + boc->inc(one_ref_cell); + boc->prepare_commit().ensure(); + boc->commit(storer).ensure(); + auto got_one_ref_cell = boc->load_cell(one_ref_cell->get_hash().as_slice()).move_as_ok(); + ASSERT_EQ(one_ref_cell->get_hash(), got_one_ref_cell->get_hash()); + boc = DynamicBagOfCellsDb::create_in_memory(kv.get(), {}); + + auto random_ref_cell = gen_random_cell(3, rnd); + boc->inc(random_ref_cell); + boc->prepare_commit().ensure(); + boc->commit(storer).ensure(); + auto got_random_ref_cell = boc->load_cell(random_ref_cell->get_hash().as_slice()).move_as_ok(); + ASSERT_EQ(random_ref_cell->get_hash(), got_random_ref_cell->get_hash()); + boc = DynamicBagOfCellsDb::create_in_memory(kv.get(), {}); +} + +int VERBOSITY_NAME(boc) = VERBOSITY_NAME(DEBUG) + 10; + +struct BocOptions { + std::shared_ptr async_executor; + std::optional o_in_memory; + td::uint64 seed{123}; + + auto create_dboc(td::KeyValueReader *kv, std::optional o_root_n) { + if (o_in_memory) { + auto res = DynamicBagOfCellsDb::create_in_memory(kv, *o_in_memory); + auto stats = res->get_stats().move_as_ok(); + if (o_root_n) { + ASSERT_EQ(*o_root_n, stats.roots_total_count); + } + VLOG(boc) << "reset roots_n=" << stats.roots_total_count << " cells_n=" << stats.cells_total_count; + return res; + } + return DynamicBagOfCellsDb::create(); + }; + void prepare_commit(DynamicBagOfCellsDb &dboc) { + if (async_executor) { + async_executor->inc_generation(); + std::latch latch(1); + td::Result res; + async_executor->execute_sync([&] { + dboc.prepare_commit_async(async_executor, [&](auto r) { + res = std::move(r); + latch.count_down(); + }); + }); + latch.wait(); + async_executor->execute_sync([&] {}); + async_executor->inc_generation(); + } else { + dboc.prepare_commit(); + } + } +}; + +template +void with_all_boc_options(F &&f, size_t tests_n = 500) { + LOG(INFO) << "Test dynamic boc"; + auto counter = [] { return td::NamedThreadSafeCounter::get_default().get_counter("DataCell").sum(); }; + auto run = [&](BocOptions options) { + LOG(INFO) << "\t" << (options.o_in_memory ? "in memory" : "on disk") << (options.async_executor ? " async" : ""); + if (options.o_in_memory) { + LOG(INFO) << "\t\tuse_arena=" << options.o_in_memory->use_arena + << " less_memory=" << options.o_in_memory->use_less_memory_during_creation; + } + for (td::uint32 i = 0; i < tests_n; i++) { + auto before = counter(); + options.seed = i == 0 ? 123 : i; + f(options); + auto after = counter(); + LOG_CHECK((options.o_in_memory && options.o_in_memory->use_arena) || before == after) + << before << " vs " << after; + } + }; + run({.async_executor = std::make_shared(4)}); + run({}); + for (auto use_arena : {false, true}) { + for (auto less_memory : {false, true}) { + run({.o_in_memory = + DynamicBagOfCellsDb::CreateInMemoryOptions{.extra_threads = std::thread::hardware_concurrency(), + .verbose = false, + .use_arena = use_arena, + .use_less_memory_during_creation = less_memory}}); + } + } +} + +void test_dynamic_boc(BocOptions options) { + auto counter = [] { return td::NamedThreadSafeCounter::get_default().get_counter("DataCell").sum(); }; + auto before = counter(); + SCOPE_EXIT { + LOG_CHECK((options.o_in_memory && options.o_in_memory->use_arena) || before == counter()) + << before << " vs " << counter(); + }; + td::Random::Xorshift128plus rnd{options.seed}; std::string old_root_hash; std::string old_root_serialization; auto kv = std::make_shared(); - auto dboc = DynamicBagOfCellsDb::create(); + auto create_dboc = [&]() { + auto roots_n = old_root_hash.empty() ? 0 : 1; + return options.create_dboc(kv.get(), roots_n); + }; + auto dboc = create_dboc(); dboc->set_loader(std::make_unique(kv)); for (int t = 1000; t >= 0; t--) { if (rnd() % 10 == 0) { - dboc = DynamicBagOfCellsDb::create(); + dboc = create_dboc(); } dboc->set_loader(std::make_unique(kv)); Ref old_root; @@ -795,29 +1075,41 @@ TEST(TonDb, DynamicBoc) { if (t != 0) { dboc->inc(cell); } - dboc->prepare_commit(); + dboc->prepare_commit().ensure(); { CellStorer cell_storer(*kv); - dboc->commit(cell_storer); + dboc->commit(cell_storer).ensure(); } } ASSERT_EQ(0u, kv->count("").ok()); +} + +TEST(TonDb, DynamicBoc) { + with_all_boc_options(test_dynamic_boc, 1); }; -TEST(TonDb, DynamicBoc2) { - int VERBOSITY_NAME(boc) = VERBOSITY_NAME(DEBUG) + 10; - td::Random::Xorshift128plus rnd{123}; - int total_roots = 10000; - int max_roots = 20; - std::vector root_hashes(max_roots); - std::vector> roots(max_roots); +void test_dynamic_boc2(BocOptions options) { + td::Random::Xorshift128plus rnd{options.seed}; + + int total_roots = rnd.fast(1, !rnd.fast(0, 10) * 100 + 10); + int max_roots = rnd.fast(1, 20); int last_commit_at = 0; int first_root_id = 0; int last_root_id = 0; auto kv = std::make_shared(); - auto dboc = DynamicBagOfCellsDb::create(); + auto create_dboc = [&](td::int64 root_n) { return options.create_dboc(kv.get(), root_n); }; + auto dboc = create_dboc(0); dboc->set_loader(std::make_unique(kv)); + auto counter = [] { return td::NamedThreadSafeCounter::get_default().get_counter("DataCell").sum(); }; + auto before = counter(); + SCOPE_EXIT{ + // LOG_CHECK((options.o_in_memory && options.o_in_memory->use_arena) || before == counter()) + // << before << " vs " << counter(); + }; + + std::vector> roots(max_roots); + std::vector root_hashes(max_roots); auto add_root = [&](Ref root) { dboc->inc(root); root_hashes[last_root_id % max_roots] = (root->get_hash().as_slice().str()); @@ -825,18 +1117,23 @@ TEST(TonDb, DynamicBoc2) { last_root_id++; }; - auto get_root = [&](int root_id) { + auto get_root = [&](int root_id) -> Ref { VLOG(boc) << " from older root #" << root_id; auto from_root = roots[root_id % max_roots]; if (from_root.is_null()) { VLOG(boc) << " from db"; auto from_root_hash = root_hashes[root_id % max_roots]; - from_root = dboc->load_cell(from_root_hash).move_as_ok(); + if (rnd() % 2 == 0) { + from_root = dboc->load_root(from_root_hash).move_as_ok(); + } else { + from_root = dboc->load_cell(from_root_hash).move_as_ok(); + } } else { VLOG(boc) << "FROM MEMORY"; } return from_root; }; + std::map root_cnt; auto new_root = [&] { if (last_root_id == total_roots) { return; @@ -850,13 +1147,16 @@ TEST(TonDb, DynamicBoc2) { from_root = get_root(rnd.fast(first_root_id, last_root_id - 1)); } VLOG(boc) << " ..."; - add_root(gen_random_cell(rnd.fast(1, 20), from_root, rnd)); + auto new_root = gen_random_cell(rnd.fast(1, 20), from_root, rnd); + root_cnt[new_root->get_hash()]++; + add_root(std::move(new_root)); VLOG(boc) << " OK"; }; auto commit = [&] { VLOG(boc) << "commit"; - dboc->prepare_commit(); + //rnd.fast(0, 1); + options.prepare_commit(*dboc); { CellStorer cell_storer(*kv); dboc->commit(cell_storer); @@ -870,7 +1170,7 @@ TEST(TonDb, DynamicBoc2) { auto reset = [&] { VLOG(boc) << "reset"; commit(); - dboc = DynamicBagOfCellsDb::create(); + dboc = create_dboc(td::int64(root_cnt.size())); dboc->set_loader(std::make_unique(kv)); }; @@ -879,7 +1179,15 @@ TEST(TonDb, DynamicBoc2) { if (first_root_id == last_root_id) { return; } - dboc->dec(get_root(first_root_id)); + auto old_root = get_root(first_root_id); + auto it = root_cnt.find(old_root->get_hash()); + it->second--; + CHECK(it->second >= 0); + if (it->second == 0) { + root_cnt.erase(it); + } + + dboc->dec(std::move(old_root)); first_root_id++; VLOG(boc) << " OK"; }; @@ -893,6 +1201,10 @@ TEST(TonDb, DynamicBoc2) { ASSERT_EQ(0u, kv->count("").ok()); } +TEST(TonDb, DynamicBoc2) { + with_all_boc_options(test_dynamic_boc2); +} + template td::Status test_boc_deserializer(std::vector> cells, int mode) { auto total_data_cells_before = vm::DataCell::get_total_data_cells(); @@ -1848,7 +2160,7 @@ TEST(TonDb, CompactArrayOld) { SCOPE_EXIT { ton_db->commit_transaction(std::move(txn)); }; - auto smart = txn->begin_smartcontract(""); + auto smart = txn->begin_smartcontract(); SCOPE_EXIT { txn->commit_smartcontract(std::move(smart)); }; @@ -1875,7 +2187,7 @@ TEST(TonDb, CompactArrayOld) { SCOPE_EXIT { ton_db->commit_transaction(std::move(txn)); }; - auto smart = txn->begin_smartcontract(""); + auto smart = txn->begin_smartcontract(); //smart->validate_meta(); SCOPE_EXIT { txn->commit_smartcontract(std::move(smart)); @@ -1896,7 +2208,7 @@ TEST(TonDb, CompactArrayOld) { SCOPE_EXIT { ton_db->abort_transaction(std::move(txn)); }; - auto smart = txn->begin_smartcontract(""); + auto smart = txn->begin_smartcontract(); SCOPE_EXIT { txn->abort_smartcontract(std::move(smart)); }; @@ -1950,17 +2262,18 @@ TEST(TonDb, BocRespectsUsageCell) { ASSERT_STREQ(serialization, serialization_of_virtualized_cell); } -TEST(TonDb, DynamicBocRespectsUsageCell) { - td::Random::Xorshift128plus rnd(123); +void test_dynamic_boc_respectes_usage_cell(vm::BocOptions options) { + td::Random::Xorshift128plus rnd(options.seed); auto cell = vm::gen_random_cell(20, rnd, true); auto usage_tree = std::make_shared(); auto usage_cell = vm::UsageCell::create(cell, usage_tree->root_ptr()); auto kv = std::make_shared(); - auto dboc = vm::DynamicBagOfCellsDb::create(); + auto dboc = options.create_dboc(kv.get(), {}); dboc->set_loader(std::make_unique(kv)); dboc->inc(usage_cell); { + options.prepare_commit(*dboc); vm::CellStorer cell_storer(*kv); dboc->commit(cell_storer); } @@ -1972,6 +2285,42 @@ TEST(TonDb, DynamicBocRespectsUsageCell) { ASSERT_STREQ(serialization, serialization_of_virtualized_cell); } +TEST(TonDb, DynamicBocRespectsUsageCell) { + vm::with_all_boc_options(test_dynamic_boc_respectes_usage_cell, 20); +} + +TEST(TonDb, LargeBocSerializer) { + td::Random::Xorshift128plus rnd{123}; + size_t n = 1000000; + std::vector data(n); + std::iota(data.begin(), data.end(), 0); + vm::CompactArray arr(data); + auto root = arr.root(); + std::string path = "serialization"; + td::unlink(path).ignore(); + auto fd = td::FileFd::open(path, td::FileFd::Flags::Create | td::FileFd::Flags::Truncate | td::FileFd::Flags::Write) + .move_as_ok(); + std_boc_serialize_to_file(root, fd, 31); + fd.close(); + auto a = td::read_file_str(path).move_as_ok(); + + auto kv = std::make_shared(); + auto dboc = vm::DynamicBagOfCellsDb::create(); + dboc->set_loader(std::make_unique(kv)); + dboc->inc(root); + dboc->prepare_commit(); + vm::CellStorer cell_storer(*kv); + dboc->commit(cell_storer); + dboc->set_loader(std::make_unique(kv)); + td::unlink(path).ignore(); + fd = td::FileFd::open(path, td::FileFd::Flags::Create | td::FileFd::Flags::Truncate | td::FileFd::Flags::Write) + .move_as_ok(); + std_boc_serialize_to_file_large(dboc->get_cell_db_reader(), root->get_hash(), fd, 31); + fd.close(); + auto b = td::read_file_str(path).move_as_ok(); + CHECK(a == b); +} + TEST(TonDb, DoNotMakeListsPrunned) { auto cell = vm::CellBuilder().store_bytes("abc").finalize(); auto is_prunned = [&](const td::Ref &cell) { return true; }; @@ -2020,7 +2369,7 @@ TEST(TonDb, CellStat) { ASSERT_EQ(stat.cells, new_stat.get_stat().cells); ASSERT_EQ(stat.bits, new_stat.get_stat().bits); - CHECK(usage_tree.unique()); + CHECK(usage_tree.use_count() == 1); usage_tree.reset(); td::Ref C, BC, C_proof; std::shared_ptr usage_tree_B; @@ -2057,7 +2406,6 @@ TEST(Ref, AtomicRef) { int threads_n = 10; std::vector nodes(threads_n); std::vector threads(threads_n); - int thread_id = 0; for (auto &thread : threads) { thread = td::thread([&] { for (int i = 0; i < 1000000; i++) { @@ -2072,7 +2420,6 @@ TEST(Ref, AtomicRef) { } } }); - thread_id++; } for (auto &thread : threads) { thread.join(); diff --git a/crypto/tl/tlbc-gen-cpp.cpp b/crypto/tl/tlbc-gen-cpp.cpp index 6edd0a121..55b4a1c05 100644 --- a/crypto/tl/tlbc-gen-cpp.cpp +++ b/crypto/tl/tlbc-gen-cpp.cpp @@ -1316,7 +1316,7 @@ void CppTypeCode::clear_context() { std::string CppTypeCode::new_tmp_var() { char buffer[16]; while (true) { - sprintf(buffer, "t%d", ++tmp_ints); + snprintf(buffer, sizeof(buffer), "t%d", ++tmp_ints); if (tmp_cpp_ids.is_good_ident(buffer) && local_cpp_ids.is_good_ident(buffer)) { break; } diff --git a/crypto/tl/tlbc.cpp b/crypto/tl/tlbc.cpp index b48bc472e..0050e1610 100644 --- a/crypto/tl/tlbc.cpp +++ b/crypto/tl/tlbc.cpp @@ -420,7 +420,7 @@ void AdmissibilityInfo::operator|=(const AdmissibilityInfo& other) { std::size_t i, j, n = info.size(), n1 = other.info.size(); assert(n1 && !(n1 & (n1 - 1))); for (i = j = 0; i < n; i++) { - info[i] = info[i] | other.info[j]; + info[i] = info[i] || other.info[j]; j = (j + 1) & (n1 - 1); } } @@ -2511,7 +2511,7 @@ void define_builtins() { Bits_type = define_builtin_type("bits", "#", false, 1023, 0, true, 0); for (int i = 1; i <= 257; i++) { char buff[8]; - sprintf(buff, "uint%d", i); + snprintf(buff, sizeof(buff), "uint%d", i); define_builtin_type(buff + 1, "", false, i, i, true, -1); if (i < 257) { define_builtin_type(buff, "", false, i, i, true, 1); @@ -2519,7 +2519,7 @@ void define_builtins() { } for (int i = 1; i <= 1023; i++) { char buff[12]; - sprintf(buff, "bits%d", i); + snprintf(buff, sizeof(buff), "bits%d", i); define_builtin_type(buff, "", false, i, i, true, 0); } Eq_type = define_builtin_type("=", "##", false, 0, 0, true); diff --git a/crypto/util/mintless-proof-generator.cpp b/crypto/util/mintless-proof-generator.cpp new file mode 100644 index 000000000..62b5f0d66 --- /dev/null +++ b/crypto/util/mintless-proof-generator.cpp @@ -0,0 +1,395 @@ +/* + 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 . +*/ + +#include "block-parse.h" +#include "block.h" +#include "td/actor/core/Actor.h" +#include "td/db/utils/BlobView.h" + +#include +#include "td/utils/OptionParser.h" +#include "td/utils/Time.h" +#include "td/utils/base64.h" +#include "td/utils/filesystem.h" +#include "td/utils/logging.h" +#include "vm/cells/MerkleProof.h" +#include "vm/db/StaticBagOfCellsDb.h" + +#include +#include + +const size_t KEY_LEN = 3 + 8 + 256; + +void print_help() { + std::cerr << "mintless-proof-generator - generates proofs for mintless jettons. Usage:\n\n"; + std::cerr << "mintless-proof-generator generate \n"; + std::cerr << " Generate a full tree for , save boc to .\n"; + std::cerr << " Input format: each line is
.\n\n"; + std::cerr << "mintless-proof-generator make_proof
.\n"; + std::cerr << " Generate a proof for address
from tree , save boc to file .\n\n"; + std::cerr << "mintless-proof-generator parse \n"; + std::cerr << " Read a tree from and output it as text to .\n"; + std::cerr << " Output format: same as input for 'generate'.\n\n"; + std::cerr << "mintless-proof-generator make_all_proofs [--threads ]\n"; + std::cerr << " Read a tree from and output proofs for all accounts to .\n"; + std::cerr << " Output format:
,\n"; + std::cerr << " Default : 1\n"; + exit(2); +} + +void log_mem_stat() { + auto r_stat = td::mem_stat(); + if (r_stat.is_error()) { + LOG(WARNING) << "Memory: " << r_stat.move_as_error(); + return; + } + auto stat = r_stat.move_as_ok(); + LOG(WARNING) << "Memory: " + << "res=" << stat.resident_size_ << " (peak=" << stat.resident_size_peak_ + << ") virt=" << stat.virtual_size_ << " (peak=" << stat.virtual_size_peak_ << ")"; +} + +td::BitArray address_to_key(const block::StdAddress &address) { + // addr_std$10 anycast:(Maybe Anycast) workchain_id:int8 address:bits256 = MsgAddressInt; + vm::CellBuilder cb; + cb.store_long(0b100, 3); + cb.store_long(address.workchain, 8); + cb.store_bits(address.addr.as_bitslice()); + return cb.data_bits(); +} + +block::StdAddress key_to_address(const td::BitArray &key) { + block::StdAddress addr; + td::ConstBitPtr ptr = key.bits(); + LOG_CHECK(ptr.get_uint(3) == 0b100) << "Invalid address"; + ptr.advance(3); + addr.workchain = (ton::WorkchainId)ptr.get_int(8); + ptr.advance(8); + addr.addr = ptr; + return addr; +} + +struct Entry { + block::StdAddress address; + td::RefInt256 amount; + td::uint64 start_from = 0, expired_at = 0; + + td::BitArray get_key() const { + return address_to_key(address); + } + + td::Ref get_value() const { + // _ amount:Coins start_from:uint48 expired_at:uint48 = AirdropItem; + vm::CellBuilder cb; + bool ok = block::tlb::t_Grams.store_integer_value(cb, *amount) && cb.store_ulong_rchk_bool(start_from, 48) && + cb.store_ulong_rchk_bool(expired_at, 48); + LOG_CHECK(ok) << "Failed to serialize AirdropItem"; + return cb.as_cellslice_ref(); + } + + static Entry parse(const td::BitArray &key, vm::CellSlice value) { + Entry e; + e.address = key_to_address(key); + bool ok = block::tlb::t_Grams.as_integer_skip_to(value, e.amount) && value.fetch_uint_to(48, e.start_from) && + value.fetch_uint_to(48, e.expired_at) && value.empty_ext(); + LOG_CHECK(ok) << "Failed to parse AirdropItem"; + return e; + } +}; + +bool read_entry(std::istream &f, Entry &entry) { + std::string line; + while (std::getline(f, line)) { + std::vector v = td::full_split(line, ' '); + if (v.empty()) { + continue; + } + auto S = [&]() -> td::Status { + if (v.size() != 4) { + return td::Status::Error("Invalid line in input"); + } + TRY_RESULT_PREFIX_ASSIGN(entry.address, block::StdAddress::parse(v[0]), "Invalid address in input: "); + entry.amount = td::string_to_int256(v[1]); + if (entry.amount.is_null() || !entry.amount->is_valid() || entry.amount->sgn() < 0) { + return td::Status::Error(PSTRING() << "Invalid amount in input: " << v[1]); + } + TRY_RESULT_PREFIX_ASSIGN(entry.start_from, td::to_integer_safe(v[2]), + "Invalid start_from in input: "); + TRY_RESULT_PREFIX_ASSIGN(entry.expired_at, td::to_integer_safe(v[3]), + "Invalid expired_at in input: "); + return td::Status::OK(); + }(); + S.ensure(); + return true; + } + return false; +} + +td::Status run_generate(std::string in_filename, std::string out_filename) { + LOG(INFO) << "Generating tree from " << in_filename; + std::ifstream in_file{in_filename}; + LOG_CHECK(in_file.is_open()) << "Cannot open file " << in_filename; + + Entry entry; + vm::Dictionary dict{KEY_LEN}; + td::uint64 count = 0; + td::Timestamp log_at = td::Timestamp::in(5.0); + while (read_entry(in_file, entry)) { + ++count; + bool ok = dict.set(entry.get_key(), entry.get_value(), vm::DictionaryBase::SetMode::Add); + LOG_CHECK(ok) << "Failed to add entry " << entry.address.rserialize() << " (line #" << count << ")"; + if (log_at.is_in_past()) { + LOG(INFO) << "Added " << count << " entries"; + log_at = td::Timestamp::in(5.0); + } + } + LOG_CHECK(in_file.eof()) << "Failed to read file " << in_filename; + in_file.close(); + + LOG_CHECK(count != 0) << "Input is empty"; + td::Ref root = dict.get_root_cell(); + LOG(INFO) << "Total: " << count << " entries, root hash: " << root->get_hash().to_hex(); + vm::BagOfCells boc; + boc.add_root(root); + TRY_STATUS(boc.import_cells()); + LOG(INFO) << "Writing to " << out_filename; + TRY_RESULT(fd, td::FileFd::open(out_filename, td::FileFd::Write | td::FileFd::Truncate | td::FileFd::Create)); + TRY_STATUS(boc.serialize_to_file(fd, 31)); + TRY_STATUS(fd.sync()); + fd.close(); + log_mem_stat(); + return td::Status::OK(); +} + +td::Status run_make_proof(std::string in_filename, std::string s_address, std::string out_filename) { + LOG(INFO) << "Generating proof for " << s_address << ", input file is " << in_filename; + TRY_RESULT(address, block::StdAddress::parse(s_address)); + + TRY_RESULT(blob_view, td::FileBlobView::create(in_filename)); + TRY_RESULT(boc, vm::StaticBagOfCellsDbLazy::create(std::move(blob_view))); + TRY_RESULT(root, boc->get_root_cell(0)); + + vm::MerkleProofBuilder mpb{root}; + vm::Dictionary dict{mpb.root(), KEY_LEN}; + auto key = address_to_key(address); + td::Ref value = dict.lookup(key); + LOG_CHECK(value.not_null()) << "No entry for address " << s_address; + Entry e = Entry::parse(key, *value); + LOG(INFO) << "Entry: address=" << e.address.workchain << ":" << e.address.addr.to_hex() + << " amount=" << e.amount->to_dec_string() << " start_from=" << e.start_from + << " expire_at=" << e.expired_at; + + TRY_RESULT(proof, mpb.extract_proof_boc()); + LOG(INFO) << "Writing proof to " << out_filename << " (" << td::format::as_size(proof.size()) << ")"; + TRY_STATUS(td::write_file(out_filename, proof)); + log_mem_stat(); + return td::Status::OK(); +} + +td::Status run_parse(std::string in_filename, std::string out_filename) { + LOG(INFO) << "Parsing " << in_filename; + std::ofstream out_file{out_filename}; + LOG_CHECK(out_file.is_open()) << "Cannot open file " << out_filename; + + TRY_RESULT(blob_view, td::FileBlobView::create(in_filename)); + TRY_RESULT(boc, vm::StaticBagOfCellsDbLazy::create(std::move(blob_view))); + TRY_RESULT(root, boc->get_root_cell(0)); + LOG(INFO) << "Root hash = " << root->get_hash().to_hex(); + vm::Dictionary dict{root, KEY_LEN}; + td::Timestamp log_at = td::Timestamp::in(5.0); + td::uint64 count = 0; + bool ok = dict.check_for_each([&](td::Ref value, td::ConstBitPtr key, int key_len) { + CHECK(key_len == KEY_LEN); + Entry e = Entry::parse(key, *value); + out_file << e.address.workchain << ":" << e.address.addr.to_hex() << " " << e.amount->to_dec_string() << " " + << e.start_from << " " << e.expired_at << "\n"; + LOG_CHECK(!out_file.fail()) << "Failed to write to " << out_filename; + ++count; + if (log_at.is_in_past()) { + LOG(INFO) << "Parsed " << count << " entries"; + log_at = td::Timestamp::in(5.0); + } + return true; + }); + LOG_CHECK(ok) << "Failed to parse dictionary"; + out_file.close(); + LOG_CHECK(!out_file.fail()) << "Failed to write to " << out_filename; + LOG(INFO) << "Written " << count << " entries to " << out_filename; + log_mem_stat(); + return td::Status::OK(); +} + +class MakeAllProofsActor : public td::actor::core::Actor { + public: + MakeAllProofsActor(std::string in_filename, std::string out_filename, int max_workers) + : in_filename_(in_filename), out_filename_(out_filename), max_workers_(max_workers) { + } + + void start_up() override { + auto S = [&]() -> td::Status { + out_file_.open(out_filename_); + LOG_CHECK(out_file_.is_open()) << "Cannot open file " << out_filename_; + LOG(INFO) << "Reading " << in_filename_; + TRY_RESULT(blob_view, td::FileBlobView::create(in_filename_)); + TRY_RESULT(boc, vm::StaticBagOfCellsDbLazy::create(std::move(blob_view))); + TRY_RESULT(root, boc->get_root_cell(0)); + LOG(INFO) << "Root hash = " << root->get_hash().to_hex(); + dict_ = vm::Dictionary{root, KEY_LEN}; + return td::Status::OK(); + }(); + S.ensure(); + run(); + alarm_timestamp() = td::Timestamp::in(5.0); + } + + void alarm() override { + alarm_timestamp() = td::Timestamp::in(5.0); + LOG(INFO) << "Processed " << written_count_ << " entries"; + } + + void run() { + for (auto it = pending_results_.begin(); it != pending_results_.end() && !it->second.empty();) { + out_file_ << it->second << "\n"; + LOG_CHECK(!out_file_.fail()) << "Failed to write to " << out_filename_; + it = pending_results_.erase(it); + ++written_count_; + } + while (active_workers_ < max_workers_ && !eof_) { + td::Ref value = dict_.lookup_nearest_key(current_key_, true, current_idx_ == 0); + if (value.is_null()) { + eof_ = true; + break; + } + run_worker(current_key_, current_idx_); + ++current_idx_; + ++active_workers_; + } + if (eof_ && active_workers_ == 0) { + out_file_.close(); + LOG_CHECK(!out_file_.fail()) << "Failed to write to " << out_filename_; + LOG(INFO) << "Written " << written_count_ << " entries to " << out_filename_; + stop(); + td::actor::SchedulerContext::get()->stop(); + } + } + + void run_worker(td::BitArray key, td::uint64 idx) { + pending_results_[idx] = ""; + ton::delay_action( + [SelfId = actor_id(this), key, idx, root = dict_.get_root_cell()]() { + vm::MerkleProofBuilder mpb{root}; + CHECK(vm::Dictionary(mpb.root(), KEY_LEN).lookup(key).not_null()); + auto r_proof = mpb.extract_proof_boc(); + r_proof.ensure(); + block::StdAddress addr = key_to_address(key); + std::string result = PSTRING() << addr.workchain << ":" << addr.addr.to_hex() << "," + << td::base64_encode(r_proof.move_as_ok()); + td::actor::send_closure(SelfId, &MakeAllProofsActor::on_result, idx, std::move(result)); + }, + td::Timestamp::now()); + } + + void on_result(td::uint64 idx, std::string result) { + pending_results_[idx] = std::move(result); + --active_workers_; + run(); + } + + private: + std::string in_filename_, out_filename_; + int max_workers_; + + std::ofstream out_file_; + vm::Dictionary dict_{KEY_LEN}; + td::BitArray current_key_ = td::BitArray::zero(); + td::uint64 current_idx_ = 0; + bool eof_ = false; + int active_workers_ = 0; + + std::map pending_results_; + td::uint64 written_count_ = 0; +}; + +td::Status run_make_all_proofs(std::string in_filename, std::string out_filename, int threads) { + td::actor::Scheduler scheduler({(size_t)threads}); + scheduler.run_in_context( + [&] { td::actor::create_actor("proofs", in_filename, out_filename, threads).release(); }); + while (scheduler.run(1)) { + } + log_mem_stat(); + return td::Status::OK(); +} + +int main(int argc, char *argv[]) { + SET_VERBOSITY_LEVEL(verbosity_INFO); + td::set_log_fatal_error_callback([](td::CSlice) { exit(2); }); + if (argc <= 1) { + print_help(); + return 2; + } + + std::string command = argv[1]; + try { + if (command == "generate") { + if (argc != 4) { + print_help(); + } + run_generate(argv[2], argv[3]).ensure(); + return 0; + } + if (command == "make_proof") { + if (argc != 5) { + print_help(); + } + run_make_proof(argv[2], argv[3], argv[4]).ensure(); + return 0; + } + if (command == "parse") { + if (argc != 4) { + print_help(); + } + run_parse(argv[2], argv[3]).ensure(); + return 0; + } + + if (command == "make_all_proofs") { + std::vector args; + int threads = 1; + for (int i = 2; i < argc; ++i) { + if (!strcmp(argv[i], "--threads")) { + ++i; + auto r = td::to_integer_safe(td::as_slice(argv[i])); + LOG_CHECK(r.is_ok() && r.ok() >= 1 && r.ok() <= 127) << " should be in [1..127]"; + threads = r.move_as_ok(); + } else { + args.push_back(argv[i]); + } + } + if (args.size() != 2) { + print_help(); + } + run_make_all_proofs(args[0], args[1], threads).ensure(); + return 0; + } + } catch (vm::VmError &e) { + LOG(FATAL) << "VM error: " << e.get_msg(); + } catch (vm::VmVirtError &e) { + LOG(FATAL) << "VM error: " << e.get_msg(); + } + + LOG(FATAL) << "Unknown command '" << command << "'"; +} diff --git a/crypto/vm/atom.cpp b/crypto/vm/atom.cpp index a7d5486a1..dbf1b16f5 100644 --- a/crypto/vm/atom.cpp +++ b/crypto/vm/atom.cpp @@ -35,7 +35,7 @@ void Atom::print_to(std::ostream& os) const { std::string Atom::make_name() const { char buffer[16]; - sprintf(buffer, "atom#%d", index_); + snprintf(buffer, sizeof(buffer), "atom#%d", index_); return buffer; } diff --git a/crypto/vm/boc.cpp b/crypto/vm/boc.cpp index bd334cbfc..7ec8bdd1d 100644 --- a/crypto/vm/boc.cpp +++ b/crypto/vm/boc.cpp @@ -183,6 +183,9 @@ int BagOfCells::add_root(td::Ref add_root) { // Changes in this function may require corresponding changes in crypto/vm/large-boc-serializer.cpp td::Status BagOfCells::import_cells() { + if (logger_ptr_) { + logger_ptr_->start_stage("import_cells"); + } cells_clear(); for (auto& root : roots) { auto res = import_cell(root.cell, 0); @@ -196,6 +199,9 @@ td::Status BagOfCells::import_cells() { //LOG(INFO) << "[cells: " << cell_count << ", refs: " << int_refs << ", bytes: " << data_bytes //<< ", internal hashes: " << int_hashes << ", top hashes: " << top_hashes << "]"; CHECK(cell_count != 0); + if (logger_ptr_) { + logger_ptr_->finish_stage(PSLICE() << cell_count << " cells"); + } return td::Status::OK(); } @@ -207,6 +213,9 @@ td::Result BagOfCells::import_cell(td::Ref cell, int depth) { if (cell.is_null()) { return td::Status::Error("error while importing a cell into a bag of cells: cell is null"); } + if (logger_ptr_) { + TRY_STATUS(logger_ptr_->on_cell_processed()); + } auto it = cells.find(cell->get_hash()); if (it != cells.end()) { auto pos = it->second; @@ -436,17 +445,19 @@ std::size_t BagOfCells::estimate_serialized_size(int mode) { return res.ok(); } -BagOfCells& BagOfCells::serialize(int mode) { +td::Status BagOfCells::serialize(int mode) { std::size_t size_est = estimate_serialized_size(mode); if (!size_est) { serialized.clear(); - return *this; + return td::Status::OK(); } serialized.resize(size_est); - if (serialize_to(const_cast(serialized.data()), serialized.size(), mode) != size_est) { + TRY_RESULT(size, serialize_to(const_cast(serialized.data()), serialized.size(), mode)); + if (size != size_est) { serialized.clear(); + return td::Status::Error("serialization failed"); } - return *this; + return td::Status::OK(); } std::string BagOfCells::serialize_to_string(int mode) { @@ -456,8 +467,8 @@ std::string BagOfCells::serialize_to_string(int mode) { } std::string res; res.resize(size_est, 0); - if (serialize_to(const_cast(reinterpret_cast(res.data())), res.size(), mode) == - res.size()) { + if (serialize_to(const_cast(reinterpret_cast(res.data())), res.size(), mode) + .move_as_ok() == res.size()) { return res; } else { return {}; @@ -470,8 +481,9 @@ td::Result BagOfCells::serialize_to_slice(int mode) { return td::Status::Error("no cells to serialize to this bag of cells"); } td::BufferSlice res(size_est); - if (serialize_to(const_cast(reinterpret_cast(res.data())), res.size(), mode) == - res.size()) { + TRY_RESULT(size, serialize_to(const_cast(reinterpret_cast(res.data())), + res.size(), mode)); + if (size == res.size()) { return std::move(res); } else { return td::Status::Error("error while serializing a bag of cells: actual serialized size differs from estimated"); @@ -494,14 +506,10 @@ std::string BagOfCells::extract_string() const { // cell_data:(tot_cells_size * [ uint8 ]) // = BagOfCells; // Changes in this function may require corresponding changes in crypto/vm/large-boc-serializer.cpp -template -std::size_t BagOfCells::serialize_to_impl(WriterT& writer, int mode) { - auto store_ref = [&](unsigned long long value) { - writer.store_uint(value, info.ref_byte_size); - }; - auto store_offset = [&](unsigned long long value) { - writer.store_uint(value, info.offset_byte_size); - }; +template +td::Result BagOfCells::serialize_to_impl(WriterT& writer, int mode) { + auto store_ref = [&](unsigned long long value) { writer.store_uint(value, info.ref_byte_size); }; + auto store_offset = [&](unsigned long long value) { writer.store_uint(value, info.offset_byte_size); }; writer.store_uint(info.magic, 4); @@ -536,6 +544,9 @@ std::size_t BagOfCells::serialize_to_impl(WriterT& writer, int mode) { DCHECK((unsigned)cell_count == cell_list_.size()); if (info.has_index) { std::size_t offs = 0; + if (logger_ptr_) { + logger_ptr_->start_stage("generate_index"); + } for (int i = cell_count - 1; i >= 0; --i) { const Ref& dc = cell_list_[i].dc_ref; bool with_hash = (mode & Mode::WithIntHashes) && !cell_list_[i].wt; @@ -548,11 +559,20 @@ std::size_t BagOfCells::serialize_to_impl(WriterT& writer, int mode) { fixed_offset = offs * 2 + cell_list_[i].should_cache; } store_offset(fixed_offset); + if (logger_ptr_) { + TRY_STATUS(logger_ptr_->on_cell_processed()); + } + } + if (logger_ptr_) { + logger_ptr_->finish_stage(""); } DCHECK(offs == info.data_size); } DCHECK(writer.position() == info.data_offset); size_t keep_position = writer.position(); + if (logger_ptr_) { + logger_ptr_->start_stage("serialize"); + } for (int i = 0; i < cell_count; ++i) { const auto& dc_info = cell_list_[cell_count - 1 - i]; const Ref& dc = dc_info.dc_ref; @@ -572,6 +592,9 @@ std::size_t BagOfCells::serialize_to_impl(WriterT& writer, int mode) { // std::cerr << ' ' << k; } // std::cerr << std::endl; + if (logger_ptr_) { + TRY_STATUS(logger_ptr_->on_cell_processed()); + } } writer.chk(); DCHECK(writer.position() - keep_position == info.data_size); @@ -580,11 +603,14 @@ std::size_t BagOfCells::serialize_to_impl(WriterT& writer, int mode) { unsigned crc = writer.get_crc32(); writer.store_uint(td::bswap32(crc), 4); } + if (logger_ptr_) { + logger_ptr_->finish_stage(PSLICE() << cell_count << " cells, " << writer.position() << " bytes"); + } DCHECK(writer.empty()); return writer.position(); } -std::size_t BagOfCells::serialize_to(unsigned char* buffer, std::size_t buff_size, int mode) { +td::Result BagOfCells::serialize_to(unsigned char* buffer, std::size_t buff_size, int mode) { std::size_t size_est = estimate_serialized_size(mode); if (!size_est || size_est > buff_size) { return 0; @@ -599,7 +625,7 @@ td::Status BagOfCells::serialize_to_file(td::FileFd& fd, int mode) { return td::Status::Error("no cells to serialize to this bag of cells"); } boc_writers::FileWriter writer{fd, size_est}; - size_t s = serialize_to_impl(writer, mode); + TRY_RESULT(s, serialize_to_impl(writer, mode)); TRY_STATUS(writer.finalize()); if (s != size_est) { return td::Status::Error("error while serializing a bag of cells: actual serialized size differs from estimated"); @@ -1001,6 +1027,21 @@ td::Result std_boc_serialize_multi(std::vector> roots } return boc.serialize_to_slice(mode); } +td::Status std_boc_serialize_to_file(Ref root, td::FileFd& fd, int mode, + td::CancellationToken cancellation_token) { + if (root.is_null()) { + return td::Status::Error("cannot serialize a null cell reference into a bag of cells"); + } + td::Timer timer; + BagOfCellsLogger logger(std::move(cancellation_token)); + BagOfCells boc; + boc.set_logger(&logger); + boc.add_root(std::move(root)); + TRY_STATUS(boc.import_cells()); + TRY_STATUS(boc.serialize_to_file(fd, mode)); + LOG(ERROR) << "serialization took " << timer.elapsed() << "s"; + return td::Status::OK(); +} /* * @@ -1214,4 +1255,35 @@ bool VmStorageStat::add_storage(const CellSlice& cs) { return true; } +static td::uint64 estimate_prunned_size() { + return 41; +} + +static td::uint64 estimate_serialized_size(const Ref& cell) { + return cell->get_serialized_size() + cell->size_refs() * 3 + 3; +} + +void ProofStorageStat::add_cell(const Ref& cell) { + auto& status = cells_[cell->get_hash()]; + if (status == c_loaded) { + return; + } + if (status == c_prunned) { + proof_size_ -= estimate_prunned_size(); + } + status = c_loaded; + proof_size_ += estimate_serialized_size(cell); + for (unsigned i = 0; i < cell->size_refs(); ++i) { + auto& child_status = cells_[cell->get_ref(i)->get_hash()]; + if (child_status == c_none) { + child_status = c_prunned; + proof_size_ += estimate_prunned_size(); + } + } +} + +td::uint64 ProofStorageStat::estimate_proof_size() const { + return proof_size_; +} + } // namespace vm diff --git a/crypto/vm/boc.h b/crypto/vm/boc.h index a5f87774d..8adf240fb 100644 --- a/crypto/vm/boc.h +++ b/crypto/vm/boc.h @@ -27,6 +27,8 @@ #include "td/utils/buffer.h" #include "td/utils/HashMap.h" #include "td/utils/HashSet.h" +#include "td/utils/Time.h" +#include "td/utils/Timer.h" #include "td/utils/port/FileFd.h" namespace vm { @@ -163,6 +165,18 @@ struct VmStorageStat { } }; +class ProofStorageStat { + public: + void add_cell(const Ref& cell); + td::uint64 estimate_proof_size() const; + private: + enum CellStatus { + c_none = 0, c_prunned = 1, c_loaded = 2 + }; + std::map cells_; + td::uint64 proof_size_ = 0; +}; + struct CellSerializationInfo { bool special; Cell::LevelMask level_mask; @@ -187,6 +201,43 @@ struct CellSerializationInfo { td::Result> create_data_cell(td::Slice data, td::Span> refs) const; }; +class BagOfCellsLogger { + public: + BagOfCellsLogger() = default; + explicit BagOfCellsLogger(td::CancellationToken cancellation_token) + : cancellation_token_(std::move(cancellation_token)) { + } + + void start_stage(std::string stage) { + log_speed_at_ = td::Timestamp::in(LOG_SPEED_PERIOD); + processed_cells_ = 0; + timer_ = {}; + stage_ = std::move(stage); + } + void finish_stage(td::Slice desc) { + LOG(ERROR) << "serializer: " << stage_ << " took " << timer_.elapsed() << "s, " << desc; + } + td::Status on_cell_processed() { + ++processed_cells_; + if (processed_cells_ % 1000 == 0) { + TRY_STATUS(cancellation_token_.check()); + } + if (log_speed_at_.is_in_past()) { + log_speed_at_ += LOG_SPEED_PERIOD; + LOG(WARNING) << "serializer: " << stage_ << " " << (double)processed_cells_ / LOG_SPEED_PERIOD << " cells/s"; + processed_cells_ = 0; + } + return td::Status::OK(); + } + + private: + std::string stage_; + td::Timer timer_; + td::CancellationToken cancellation_token_; + td::Timestamp log_speed_at_; + size_t processed_cells_ = 0; + static constexpr double LOG_SPEED_PERIOD = 120.0; +}; class BagOfCells { public: enum { hash_bytes = vm::Cell::hash_bytes, default_max_roots = 16384 }; @@ -271,6 +322,7 @@ class BagOfCells { const unsigned char* index_ptr{nullptr}; const unsigned char* data_ptr{nullptr}; std::vector custom_index; + BagOfCellsLogger* logger_ptr_{nullptr}; public: void clear(); @@ -280,14 +332,17 @@ class BagOfCells { int add_root(td::Ref add_root); td::Status import_cells() TD_WARN_UNUSED_RESULT; BagOfCells() = default; + void set_logger(BagOfCellsLogger* logger_ptr) { + logger_ptr_ = logger_ptr; + } std::size_t estimate_serialized_size(int mode = 0); - BagOfCells& serialize(int mode = 0); - std::string serialize_to_string(int mode = 0); + td::Status serialize(int mode = 0); + td::string serialize_to_string(int mode = 0); td::Result serialize_to_slice(int mode = 0); - std::size_t serialize_to(unsigned char* buffer, std::size_t buff_size, int mode = 0); + td::Result serialize_to(unsigned char* buffer, std::size_t buff_size, int mode = 0); td::Status serialize_to_file(td::FileFd& fd, int mode = 0); - template - std::size_t serialize_to_impl(WriterT& writer, int mode = 0); + template + td::Result serialize_to_impl(WriterT& writer, int mode = 0); std::string extract_string() const; td::Result deserialize(const td::Slice& data, int max_roots = default_max_roots); @@ -333,6 +388,8 @@ td::Result>> std_boc_deserialize_multi(td::Slice data, int max_roots = BagOfCells::default_max_roots); td::Result std_boc_serialize_multi(std::vector> root, int mode = 0); +td::Status std_boc_serialize_to_file(Ref root, td::FileFd& fd, int mode = 0, + td::CancellationToken cancellation_token = {}); td::Status std_boc_serialize_to_file_large(std::shared_ptr reader, Cell::Hash root_hash, td::FileFd& fd, int mode = 0, td::CancellationToken cancellation_token = {}); diff --git a/crypto/vm/cells/Cell.h b/crypto/vm/cells/Cell.h index 03f73b14e..a75371dbb 100644 --- a/crypto/vm/cells/Cell.h +++ b/crypto/vm/cells/Cell.h @@ -19,6 +19,7 @@ #pragma once #include "common/refcnt.hpp" #include "common/bitstring.h" +#include "td/utils/HashSet.h" #include "vm/cells/CellHash.h" #include "vm/cells/CellTraits.h" @@ -86,4 +87,31 @@ class Cell : public CellTraits { }; std::ostream& operator<<(std::ostream& os, const Cell& c); + +using is_transparent = void; // Pred to use +inline vm::CellHash as_cell_hash(const Ref& cell) { + return cell->get_hash(); +} +inline vm::CellHash as_cell_hash(td::Slice hash) { + return vm::CellHash::from_slice(hash); +} +inline vm::CellHash as_cell_hash(vm::CellHash hash) { + return hash; +} +struct CellEqF { + using is_transparent = void; // Pred to use + template + bool operator()(const A& a, const B& b) const { + return as_cell_hash(a) == as_cell_hash(b); + } +}; +struct CellHashF { + using is_transparent = void; // Pred to use + using transparent_key_equal = CellEqF; + template + size_t operator()(const T& value) const { + return cell_hash_slice_hash(as_cell_hash(value).as_slice()); + } +}; +using CellHashSet = td::HashSet, CellHashF, CellEqF>; } // namespace vm diff --git a/crypto/vm/cells/CellBuilder.cpp b/crypto/vm/cells/CellBuilder.cpp index 43058531e..772b5f6b7 100644 --- a/crypto/vm/cells/CellBuilder.cpp +++ b/crypto/vm/cells/CellBuilder.cpp @@ -617,7 +617,7 @@ std::string CellBuilder::to_hex() const { int len = serialize(buff, sizeof(buff)); char hex_buff[Cell::max_serialized_bytes * 2 + 1]; for (int i = 0; i < len; i++) { - sprintf(hex_buff + 2 * i, "%02x", buff[i]); + snprintf(hex_buff + 2 * i, sizeof(hex_buff) - 2 * i, "%02x", buff[i]); } return hex_buff; } diff --git a/crypto/vm/cells/CellHash.h b/crypto/vm/cells/CellHash.h index 2cbc0e8b3..9435675f1 100644 --- a/crypto/vm/cells/CellHash.h +++ b/crypto/vm/cells/CellHash.h @@ -74,13 +74,17 @@ struct CellHash { }; } // namespace vm +inline size_t cell_hash_slice_hash(td::Slice hash) { + // use offset 8, because in db keys are grouped by first bytes. + return td::as(hash.substr(8, 8).ubegin()); +} namespace std { template <> struct hash { typedef vm::CellHash argument_type; typedef std::size_t result_type; result_type operator()(argument_type const& s) const noexcept { - return td::as(s.as_slice().ubegin()); + return cell_hash_slice_hash(s.as_slice()); } }; } // namespace std diff --git a/crypto/vm/cells/CellSlice.cpp b/crypto/vm/cells/CellSlice.cpp index 4be1667e7..466bcd8d1 100644 --- a/crypto/vm/cells/CellSlice.cpp +++ b/crypto/vm/cells/CellSlice.cpp @@ -976,7 +976,7 @@ void CellSlice::dump(std::ostream& os, int level, bool endl) const { os << "; refs: " << refs_st << ".." << refs_en; if (level > 2) { char tmp[64]; - std::sprintf(tmp, "; ptr=data+%ld; z=%016llx", + std::snprintf(tmp, sizeof(tmp), "; ptr=data+%ld; z=%016llx", static_cast(ptr && cell.not_null() ? ptr - cell->get_data() : -1), static_cast(z)); os << tmp << " (have " << size() << " bits; " << zd << " preloaded)"; } diff --git a/crypto/vm/cells/CellUsageTree.cpp b/crypto/vm/cells/CellUsageTree.cpp index 3f43ec6bd..410b3fcd6 100644 --- a/crypto/vm/cells/CellUsageTree.cpp +++ b/crypto/vm/cells/CellUsageTree.cpp @@ -22,12 +22,12 @@ namespace vm { // // CellUsageTree::NodePtr // -bool CellUsageTree::NodePtr::on_load() const { +bool CellUsageTree::NodePtr::on_load(const td::Ref& cell) const { auto tree = tree_weak_.lock(); if (!tree) { return false; } - tree->on_load(node_id_); + tree->on_load(node_id_, cell); return true; } @@ -111,8 +111,14 @@ void CellUsageTree::set_use_mark_for_is_loaded(bool use_mark) { use_mark_ = use_mark; } -void CellUsageTree::on_load(NodeId node_id) { +void CellUsageTree::on_load(NodeId node_id, const td::Ref& cell) { + if (nodes_[node_id].is_loaded) { + return; + } nodes_[node_id].is_loaded = true; + if (cell_load_callback_) { + cell_load_callback_(cell); + } } CellUsageTree::NodeId CellUsageTree::create_child(NodeId node_id, unsigned ref_id) { diff --git a/crypto/vm/cells/CellUsageTree.h b/crypto/vm/cells/CellUsageTree.h index 150dd2bda..af0f21f53 100644 --- a/crypto/vm/cells/CellUsageTree.h +++ b/crypto/vm/cells/CellUsageTree.h @@ -22,8 +22,12 @@ #include "td/utils/int_types.h" #include "td/utils/logging.h" +#include namespace vm { + +class DataCell; + class CellUsageTree : public std::enable_shared_from_this { public: using NodeId = td::uint32; @@ -38,7 +42,7 @@ class CellUsageTree : public std::enable_shared_from_this { return node_id_ == 0 || tree_weak_.expired(); } - bool on_load() const; + bool on_load(const td::Ref& cell) const; NodePtr create_child(unsigned ref_id) const; bool mark_path(CellUsageTree* master_tree) const; bool is_from_tree(const CellUsageTree* master_tree) const; @@ -59,6 +63,10 @@ class CellUsageTree : public std::enable_shared_from_this { void set_use_mark_for_is_loaded(bool use_mark = true); NodeId create_child(NodeId node_id, unsigned ref_id); + void set_cell_load_callback(std::function&)> f) { + cell_load_callback_ = std::move(f); + } + private: struct Node { bool is_loaded{false}; @@ -68,8 +76,9 @@ class CellUsageTree : public std::enable_shared_from_this { }; bool use_mark_{false}; std::vector nodes_{2}; + std::function&)> cell_load_callback_; - void on_load(NodeId node_id); + void on_load(NodeId node_id, const td::Ref& cell); NodeId create_node(NodeId parent); }; } // namespace vm diff --git a/crypto/vm/cells/CellWithStorage.h b/crypto/vm/cells/CellWithStorage.h index 1830c37d2..f3fedfc31 100644 --- a/crypto/vm/cells/CellWithStorage.h +++ b/crypto/vm/cells/CellWithStorage.h @@ -20,6 +20,15 @@ namespace vm { namespace detail { + +template +struct DefaultAllocator { + template + std::unique_ptr make_unique(ArgsT&&... args) { + return std::make_unique(std::forward(args)...); + } +}; + template class CellWithArrayStorage : public CellT { public: @@ -29,14 +38,14 @@ class CellWithArrayStorage : public CellT { ~CellWithArrayStorage() { CellT::destroy_storage(get_storage()); } - template - static std::unique_ptr create(size_t storage_size, ArgsT&&... args) { + template + static auto create(Allocator allocator, size_t storage_size, ArgsT&&... args) { static_assert(CellT::max_storage_size <= 40 * 8, ""); //size = 128 + 32 + 8; auto size = (storage_size + 7) / 8; #define CASE(size) \ case (size): \ - return std::make_unique>(std::forward(args)...); + return allocator. template make_unique>(std::forward(args)...); #define CASE2(offset) CASE(offset) CASE(offset + 1) #define CASE8(offset) CASE2(offset) CASE2(offset + 2) CASE2(offset + 4) CASE2(offset + 6) #define CASE32(offset) CASE8(offset) CASE8(offset + 8) CASE8(offset + 16) CASE8(offset + 24) @@ -48,6 +57,10 @@ class CellWithArrayStorage : public CellT { LOG(FATAL) << "TOO BIG " << storage_size; UNREACHABLE(); } + template + static std::unique_ptr create(size_t storage_size, ArgsT&&... args) { + return create(DefaultAllocator{}, storage_size, std::forward(args)...); + } private: alignas(alignof(void*)) char storage_[Size]; diff --git a/crypto/vm/cells/DataCell.cpp b/crypto/vm/cells/DataCell.cpp index 8aac575bf..4dd301616 100644 --- a/crypto/vm/cells/DataCell.cpp +++ b/crypto/vm/cells/DataCell.cpp @@ -25,7 +25,44 @@ #include "vm/cells/CellWithStorage.h" namespace vm { +thread_local bool DataCell::use_arena = false; + +namespace { +template +struct ArenaAllocator { + template + std::unique_ptr make_unique(ArgsT&&... args) { + auto* ptr = fast_alloc(sizeof(T)); + T* obj = new (ptr) T(std::forward(args)...); + return std::unique_ptr(obj); + } +private: + td::MutableSlice alloc_batch() { + size_t batch_size = 1 << 20; + auto batch = std::make_unique(batch_size); + return td::MutableSlice(batch.release(), batch_size); + } + char* fast_alloc(size_t size) { + thread_local td::MutableSlice batch; + auto aligned_size = (size + 7) / 8 * 8; + if (batch.size() < size) { + batch = alloc_batch(); + } + auto res = batch.begin(); + batch.remove_prefix(aligned_size); + return res; + } +}; +} std::unique_ptr DataCell::create_empty_data_cell(Info info) { + if (use_arena) { + ArenaAllocator allocator; + auto res = detail::CellWithArrayStorage::create(allocator, info.get_storage_size(), info); + // this is dangerous + Ref(res.get()).release(); + return res; + } + return detail::CellWithUniquePtrStorage::create(info.get_storage_size(), info); } @@ -359,7 +396,7 @@ std::string DataCell::to_hex() const { int len = serialize(buff, sizeof(buff)); char hex_buff[max_serialized_bytes * 2 + 1]; for (int i = 0; i < len; i++) { - sprintf(hex_buff + 2 * i, "%02x", buff[i]); + snprintf(hex_buff + 2 * i, sizeof(hex_buff) - 2 * i, "%02x", buff[i]); } return hex_buff; } diff --git a/crypto/vm/cells/DataCell.h b/crypto/vm/cells/DataCell.h index 933ff04a3..6d3c845fc 100644 --- a/crypto/vm/cells/DataCell.h +++ b/crypto/vm/cells/DataCell.h @@ -27,6 +27,9 @@ namespace vm { class DataCell : public Cell { public: + // NB: cells created with use_arena=true are never freed + static thread_local bool use_arena; + DataCell(const DataCell& other) = delete; ~DataCell() override; @@ -121,10 +124,6 @@ class DataCell : public Cell { void destroy_storage(char* storage); explicit DataCell(Info info); - Cell* get_ref_raw_ptr(unsigned idx) const { - DCHECK(idx < get_refs_cnt()); - return info_.get_refs(get_storage())[idx]; - } public: td::Result load_cell() const override { @@ -152,6 +151,20 @@ class DataCell : public Cell { return Ref(get_ref_raw_ptr(idx)); } + Cell* get_ref_raw_ptr(unsigned idx) const { + DCHECK(idx < get_refs_cnt()); + return info_.get_refs(get_storage())[idx]; + } + + Ref reset_ref_unsafe(unsigned idx, Ref ref, bool check_hash = true) { + CHECK(idx < get_refs_cnt()); + auto refs = info_.get_refs(get_storage()); + CHECK(!check_hash || refs[idx]->get_hash() == ref->get_hash()); + auto res = Ref(refs[idx], Ref::acquire_t{}); // call destructor + refs[idx] = ref.release(); + return res; + } + td::uint32 get_virtualization() const override { return info_.virtualization_; } @@ -173,6 +186,9 @@ class DataCell : public Cell { return ((get_bits() + 23) >> 3) + (with_hashes ? get_level_mask().get_hashes_count() * (hash_bytes + depth_bytes) : 0); } + size_t get_storage_size() const { + return info_.get_storage_size(); + } int serialize(unsigned char* buff, int buff_size, bool with_hashes = false) const; std::string serialize() const; std::string to_hex() const; @@ -207,6 +223,9 @@ class DataCell : public Cell { }; std::ostream& operator<<(std::ostream& os, const DataCell& c); +inline CellHash as_cell_hash(const Ref& cell) { + return cell->get_hash(); +} } // namespace vm diff --git a/crypto/vm/cells/MerkleProof.h b/crypto/vm/cells/MerkleProof.h index 9c50fd078..fc2cb6ebd 100644 --- a/crypto/vm/cells/MerkleProof.h +++ b/crypto/vm/cells/MerkleProof.h @@ -66,6 +66,10 @@ class MerkleProofBuilder { td::Result> extract_proof() const; bool extract_proof_to(Ref &proof_root) const; td::Result extract_proof_boc() const; + + void set_cell_load_callback(std::function&)> f) { + usage_tree->set_cell_load_callback(std::move(f)); + } }; } // namespace vm diff --git a/crypto/vm/cells/PrunnedCell.h b/crypto/vm/cells/PrunnedCell.h index e8434ae47..a58b245cc 100644 --- a/crypto/vm/cells/PrunnedCell.h +++ b/crypto/vm/cells/PrunnedCell.h @@ -30,18 +30,27 @@ struct PrunnedCellInfo { template class PrunnedCell : public Cell { public: + ExtraT& get_extra() { + return extra_; + } const ExtraT& get_extra() const { return extra_; } static td::Result>> create(const PrunnedCellInfo& prunned_cell_info, ExtraT&& extra) { + return create(detail::DefaultAllocator>(), prunned_cell_info, std::forward(extra)); + } + + template + static td::Result>> create(AllocatorT allocator, const PrunnedCellInfo& prunned_cell_info, + ExtraT&& extra) { auto level_mask = prunned_cell_info.level_mask; if (level_mask.get_level() > max_level) { return td::Status::Error("Level is too big"); } Info info(level_mask); auto prunned_cell = - detail::CellWithUniquePtrStorage>::create(info.get_storage_size(), info, std::move(extra)); + detail::CellWithArrayStorage>::create(allocator, info.get_storage_size(), info, std::move(extra)); TRY_STATUS(prunned_cell->init(prunned_cell_info)); return Ref>(prunned_cell.release(), typename Ref>::acquire_t{}); } @@ -51,6 +60,7 @@ class PrunnedCell : public Cell { } protected: + static constexpr auto max_storage_size = (max_level + 1) * (hash_bytes + sizeof(td::uint16)); struct Info { Info(LevelMask level_mask) { level_mask_ = level_mask.get_mask() & 7; diff --git a/crypto/vm/cells/UsageCell.h b/crypto/vm/cells/UsageCell.h index bf15bb56f..3e6e88982 100644 --- a/crypto/vm/cells/UsageCell.h +++ b/crypto/vm/cells/UsageCell.h @@ -39,7 +39,7 @@ class UsageCell : public Cell { // load interface td::Result load_cell() const override { TRY_RESULT(loaded_cell, cell_->load_cell()); - if (tree_node_.on_load()) { + if (tree_node_.on_load(loaded_cell.data_cell)) { CHECK(loaded_cell.tree_node.empty()); loaded_cell.tree_node = tree_node_; } diff --git a/crypto/vm/continuation.cpp b/crypto/vm/continuation.cpp index 913869808..bc67c6d22 100644 --- a/crypto/vm/continuation.cpp +++ b/crypto/vm/continuation.cpp @@ -27,8 +27,8 @@ namespace vm { -int Continuation::jump_w(VmState* st) & { - return static_cast(this)->jump(st); +td::Ref Continuation::jump_w(VmState* st, int& exitcode) & { + return static_cast(this)->jump(st, exitcode); } bool Continuation::has_c0() const { @@ -286,7 +286,7 @@ std::string QuitCont::type() const { return "vmc_quit"; } -int ExcQuitCont::jump(VmState* st) const & { +td::Ref ExcQuitCont::jump(VmState* st, int& exitcode) const& { int n = 0; try { n = st->get_stack().pop_smallint_range(0xffff); @@ -294,7 +294,8 @@ int ExcQuitCont::jump(VmState* st) const & { n = vme.get_errno(); } VM_LOG(st) << "default exception handler, terminating vm with exit code " << n; - return ~n; + exitcode = ~n; + return {}; } std::string ExcQuitCont::type() const { @@ -311,16 +312,16 @@ Ref ExcQuitCont::deserialize(CellSlice& cs, int mode) { return cs.fetch_ulong(4) == 9 ? Ref{true} : Ref{}; } -int PushIntCont::jump(VmState* st) const & { +td::Ref PushIntCont::jump(VmState* st, int& exitcode) const& { VM_LOG(st) << "execute implicit PUSH " << push_val << " (slow)"; st->get_stack().push_smallint(push_val); - return st->jump(next); + return next; } -int PushIntCont::jump_w(VmState* st) & { +td::Ref PushIntCont::jump_w(VmState* st, int& exitcode) & { VM_LOG(st) << "execute implicit PUSH " << push_val; st->get_stack().push_smallint(push_val); - return st->jump(std::move(next)); + return std::move(next); } std::string PushIntCont::type() const { @@ -345,20 +346,20 @@ Ref PushIntCont::deserialize(CellSlice& cs, int mode) { } } -int ArgContExt::jump(VmState* st) const & { +td::Ref ArgContExt::jump(VmState* st, int& exitcode) const& { st->adjust_cr(data.save); if (data.cp != -1) { st->force_cp(data.cp); } - return ext->jump(st); + return ext; } -int ArgContExt::jump_w(VmState* st) & { +td::Ref ArgContExt::jump_w(VmState* st, int& exitcode) & { st->adjust_cr(std::move(data.save)); if (data.cp != -1) { st->force_cp(data.cp); } - return st->jump_to(std::move(ext)); + return std::move(ext); } bool ArgContExt::serialize(CellBuilder& cb) const { @@ -382,32 +383,32 @@ std::string ArgContExt::type() const { return "vmc_envelope"; } -int RepeatCont::jump(VmState* st) const & { +td::Ref RepeatCont::jump(VmState* st, int& exitcode) const& { VM_LOG(st) << "repeat " << count << " more times (slow)\n"; if (count <= 0) { - return st->jump(after); + return after; } if (body->has_c0()) { - return st->jump(body); + return body; } st->set_c0(Ref{true, body, after, count - 1}); - return st->jump(body); + return body; } -int RepeatCont::jump_w(VmState* st) & { +td::Ref RepeatCont::jump_w(VmState* st, int& exitcode) & { VM_LOG(st) << "repeat " << count << " more times\n"; if (count <= 0) { body.clear(); - return st->jump(std::move(after)); + return std::move(after); } if (body->has_c0()) { after.clear(); - return st->jump(std::move(body)); + return std::move(body); } // optimization: since this is unique, reuse *this instead of creating new object --count; st->set_c0(Ref{this}); - return st->jump(body); + return body; } bool RepeatCont::serialize(CellBuilder& cb) const { @@ -443,21 +444,21 @@ int VmState::repeat(Ref body, Ref after, long long c } } -int AgainCont::jump(VmState* st) const & { +td::Ref AgainCont::jump(VmState* st, int& exitcode) const& { VM_LOG(st) << "again an infinite loop iteration (slow)\n"; if (!body->has_c0()) { st->set_c0(Ref{this}); } - return st->jump(body); + return body; } -int AgainCont::jump_w(VmState* st) & { +td::Ref AgainCont::jump_w(VmState* st, int& exitcode) & { VM_LOG(st) << "again an infinite loop iteration\n"; if (!body->has_c0()) { st->set_c0(Ref{this}); - return st->jump(body); + return body; } else { - return st->jump(std::move(body)); + return std::move(body); } } @@ -485,31 +486,31 @@ int VmState::again(Ref body) { return jump(Ref{true, std::move(body)}); } -int UntilCont::jump(VmState* st) const & { +td::Ref UntilCont::jump(VmState* st, int& exitcode) const& { VM_LOG(st) << "until loop body end (slow)\n"; if (st->get_stack().pop_bool()) { VM_LOG(st) << "until loop terminated\n"; - return st->jump(after); + return after; } if (!body->has_c0()) { st->set_c0(Ref{this}); } - return st->jump(body); + return body; } -int UntilCont::jump_w(VmState* st) & { +td::Ref UntilCont::jump_w(VmState* st, int& exitcode) & { VM_LOG(st) << "until loop body end\n"; if (st->get_stack().pop_bool()) { VM_LOG(st) << "until loop terminated\n"; body.clear(); - return st->jump(std::move(after)); + return std::move(after); } if (!body->has_c0()) { st->set_c0(Ref{this}); - return st->jump(body); + return body; } else { after.clear(); - return st->jump(std::move(body)); + return std::move(body); } } @@ -541,54 +542,54 @@ int VmState::until(Ref body, Ref after) { return jump(std::move(body)); } -int WhileCont::jump(VmState* st) const & { +td::Ref WhileCont::jump(VmState* st, int& exitcode) const& { if (chkcond) { VM_LOG(st) << "while loop condition end (slow)\n"; if (!st->get_stack().pop_bool()) { VM_LOG(st) << "while loop terminated\n"; - return st->jump(after); + return after; } if (!body->has_c0()) { st->set_c0(Ref{true, cond, body, after, false}); } - return st->jump(body); + return body; } else { VM_LOG(st) << "while loop body end (slow)\n"; if (!cond->has_c0()) { st->set_c0(Ref{true, cond, body, after, true}); } - return st->jump(cond); + return cond; } } -int WhileCont::jump_w(VmState* st) & { +td::Ref WhileCont::jump_w(VmState* st, int& exitcode) & { if (chkcond) { VM_LOG(st) << "while loop condition end\n"; if (!st->get_stack().pop_bool()) { VM_LOG(st) << "while loop terminated\n"; cond.clear(); body.clear(); - return st->jump(std::move(after)); + return std::move(after); } if (!body->has_c0()) { chkcond = false; // re-use current object since we hold the unique pointer to it st->set_c0(Ref{this}); - return st->jump(body); + return body; } else { cond.clear(); after.clear(); - return st->jump(std::move(body)); + return std::move(body); } } else { VM_LOG(st) << "while loop body end\n"; if (!cond->has_c0()) { chkcond = true; // re-use current object st->set_c0(Ref{this}); - return st->jump(cond); + return cond; } else { body.clear(); after.clear(); - return st->jump(std::move(cond)); + return std::move(cond); } } } @@ -627,16 +628,16 @@ int VmState::loop_while(Ref cond, Ref body, Ref OrdCont::jump(VmState* st, int& exitcode) const& { st->adjust_cr(data.save); st->set_code(code, data.cp); - return 0; + return {}; } -int OrdCont::jump_w(VmState* st) & { +td::Ref OrdCont::jump_w(VmState* st, int& exitcode) & { st->adjust_cr(std::move(data.save)); st->set_code(std::move(code), data.cp); - return 0; + return {}; } bool OrdCont::serialize(CellBuilder& cb) const { diff --git a/crypto/vm/continuation.h b/crypto/vm/continuation.h index 8208fc16a..0c758c922 100644 --- a/crypto/vm/continuation.h +++ b/crypto/vm/continuation.h @@ -161,8 +161,8 @@ struct ControlData { class Continuation : public td::CntObject { public: - virtual int jump(VmState* st) const & = 0; - virtual int jump_w(VmState* st) &; + virtual td::Ref jump(VmState* st, int& exitcode) const& = 0; + virtual td::Ref jump_w(VmState* st, int& exitcode) &; virtual ControlData* get_cdata() { return 0; } @@ -203,8 +203,9 @@ class QuitCont : public Continuation { QuitCont(int _code = 0) : exit_code(_code) { } ~QuitCont() override = default; - int jump(VmState* st) const & override { - return ~exit_code; + td::Ref jump(VmState* st, int& exitcode) const& override { + exitcode = ~exit_code; + return {}; } bool serialize(CellBuilder& cb) const override; static Ref deserialize(CellSlice& cs, int mode = 0); @@ -215,7 +216,7 @@ class ExcQuitCont : public Continuation { public: ExcQuitCont() = default; ~ExcQuitCont() override = default; - int jump(VmState* st) const & override; + td::Ref jump(VmState* st, int& exitcode) const& override; bool serialize(CellBuilder& cb) const override; static Ref deserialize(CellSlice& cs, int mode = 0); std::string type() const override; @@ -229,8 +230,8 @@ class PushIntCont : public Continuation { PushIntCont(int val, Ref _next) : push_val(val), next(_next) { } ~PushIntCont() override = default; - int jump(VmState* st) const & override; - int jump_w(VmState* st) & override; + td::Ref jump(VmState* st, int& exitcode) const& override; + td::Ref jump_w(VmState* st, int& exitcode) & override; bool serialize(CellBuilder& cb) const override; static Ref deserialize(CellSlice& cs, int mode = 0); std::string type() const override; @@ -245,8 +246,8 @@ class RepeatCont : public Continuation { : body(std::move(_body)), after(std::move(_after)), count(_count) { } ~RepeatCont() override = default; - int jump(VmState* st) const & override; - int jump_w(VmState* st) & override; + td::Ref jump(VmState* st, int& exitcode) const& override; + td::Ref jump_w(VmState* st, int& exitcode) & override; bool serialize(CellBuilder& cb) const override; static Ref deserialize(CellSlice& cs, int mode = 0); std::string type() const override; @@ -259,8 +260,8 @@ class AgainCont : public Continuation { AgainCont(Ref _body) : body(std::move(_body)) { } ~AgainCont() override = default; - int jump(VmState* st) const & override; - int jump_w(VmState* st) & override; + td::Ref jump(VmState* st, int& exitcode) const& override; + td::Ref jump_w(VmState* st, int& exitcode) & override; bool serialize(CellBuilder& cb) const override; static Ref deserialize(CellSlice& cs, int mode = 0); std::string type() const override; @@ -273,8 +274,8 @@ class UntilCont : public Continuation { UntilCont(Ref _body, Ref _after) : body(std::move(_body)), after(std::move(_after)) { } ~UntilCont() override = default; - int jump(VmState* st) const & override; - int jump_w(VmState* st) & override; + td::Ref jump(VmState* st, int& exitcode) const& override; + td::Ref jump_w(VmState* st, int& exitcode) & override; bool serialize(CellBuilder& cb) const override; static Ref deserialize(CellSlice& cs, int mode = 0); std::string type() const override; @@ -289,8 +290,8 @@ class WhileCont : public Continuation { : cond(std::move(_cond)), body(std::move(_body)), after(std::move(_after)), chkcond(_chk) { } ~WhileCont() override = default; - int jump(VmState* st) const & override; - int jump_w(VmState* st) & override; + td::Ref jump(VmState* st, int& exitcode) const& override; + td::Ref jump_w(VmState* st, int& exitcode) & override; bool serialize(CellBuilder& cb) const override; static Ref deserialize(CellSlice& cs, int mode = 0); std::string type() const override; @@ -312,8 +313,8 @@ class ArgContExt : public Continuation { ArgContExt(const ArgContExt&) = default; ArgContExt(ArgContExt&&) = default; ~ArgContExt() override = default; - int jump(VmState* st) const & override; - int jump_w(VmState* st) & override; + td::Ref jump(VmState* st, int& exitcode) const& override; + td::Ref jump_w(VmState* st, int& exitcode) & override; ControlData* get_cdata() override { return &data; } @@ -354,8 +355,8 @@ class OrdCont : public Continuation { td::CntObject* make_copy() const override { return new OrdCont{*this}; } - int jump(VmState* st) const & override; - int jump_w(VmState* st) & override; + td::Ref jump(VmState* st, int& exitcode) const& override; + td::Ref jump_w(VmState* st, int& exitcode) & override; ControlData* get_cdata() override { return &data; diff --git a/crypto/vm/db/CellHashTable.h b/crypto/vm/db/CellHashTable.h index 7d0308b74..522c987be 100644 --- a/crypto/vm/db/CellHashTable.h +++ b/crypto/vm/db/CellHashTable.h @@ -19,7 +19,7 @@ #pragma once #include "td/utils/Slice.h" - +#include "td/utils/HashSet.h" #include namespace vm { @@ -43,7 +43,7 @@ class CellHashTable { template void for_each(F &&f) { for (auto &info : set_) { - f(info); + f(const_cast(info)); } } template @@ -73,6 +73,6 @@ class CellHashTable { } private: - std::set> set_; + td::NodeHashSet set_; }; } // namespace vm diff --git a/crypto/vm/db/CellStorage.cpp b/crypto/vm/db/CellStorage.cpp index 303d46503..06df461ef 100644 --- a/crypto/vm/db/CellStorage.cpp +++ b/crypto/vm/db/CellStorage.cpp @@ -33,6 +33,7 @@ class RefcntCellStorer { template void store(StorerT &storer) const { + TD_PERF_COUNTER(cell_store); using td::store; if (as_boc_) { td::int32 tag = -1; @@ -151,18 +152,27 @@ CellLoader::CellLoader(std::shared_ptr reader, std::function CellLoader::load(td::Slice hash, bool need_data, ExtCellCreator &ext_cell_creator) { //LOG(ERROR) << "Storage: load cell " << hash.size() << " " << td::base64_encode(hash); - LoadResult res; + TD_PERF_COUNTER(cell_load); std::string serialized; TRY_RESULT(get_status, reader_->get(hash, serialized)); if (get_status != KeyValue::GetStatus::Ok) { DCHECK(get_status == KeyValue::GetStatus::NotFound); - return res; + return LoadResult{}; } + TRY_RESULT(res, load(hash, serialized, need_data, ext_cell_creator)); + if (on_load_callback_) { + on_load_callback_(res); + } + return res; +} +td::Result CellLoader::load(td::Slice hash, td::Slice value, bool need_data, + ExtCellCreator &ext_cell_creator) { + LoadResult res; res.status = LoadResult::Ok; RefcntCellParser refcnt_cell(need_data); - td::TlParser parser(serialized); + td::TlParser parser(value); refcnt_cell.parse(parser, ext_cell_creator); TRY_STATUS(parser.get_status()); @@ -170,13 +180,28 @@ td::Result CellLoader::load(td::Slice hash, bool need_da res.cell_ = std::move(refcnt_cell.cell); res.stored_boc_ = refcnt_cell.stored_boc_; //CHECK(res.cell_->get_hash() == hash); - if (on_load_callback_) { - on_load_callback_(res); - } return res; } +td::Result CellLoader::load_refcnt(td::Slice hash) { + LoadResult res; + std::string serialized; + TRY_RESULT(get_status, reader_->get(hash, serialized)); + if (get_status != KeyValue::GetStatus::Ok) { + DCHECK(get_status == KeyValue::GetStatus::NotFound); + return res; + } + res.status = LoadResult::Ok; + td::TlParser parser(serialized); + td::parse(res.refcnt_, parser); + if (res.refcnt_ == -1) { + parse(res.refcnt_, parser); + } + TRY_STATUS(parser.get_status()); + return res; +} + CellStorer::CellStorer(KeyValue &kv) : kv_(kv) { } @@ -184,7 +209,11 @@ td::Status CellStorer::erase(td::Slice hash) { return kv_.erase(hash); } +std::string CellStorer::serialize_value(td::int32 refcnt, const td::Ref &cell, bool as_boc) { + return td::serialize(RefcntCellStorer(refcnt, cell, as_boc)); +} + td::Status CellStorer::set(td::int32 refcnt, const td::Ref &cell, bool as_boc) { - return kv_.set(cell->get_hash().as_slice(), td::serialize(RefcntCellStorer(refcnt, cell, as_boc))); + return kv_.set(cell->get_hash().as_slice(), serialize_value(refcnt, cell, as_boc)); } } // namespace vm diff --git a/crypto/vm/db/CellStorage.h b/crypto/vm/db/CellStorage.h index 3106ee16d..cabd7fdcb 100644 --- a/crypto/vm/db/CellStorage.h +++ b/crypto/vm/db/CellStorage.h @@ -49,6 +49,8 @@ class CellLoader { }; CellLoader(std::shared_ptr reader, std::function on_load_callback = {}); td::Result load(td::Slice hash, bool need_data, ExtCellCreator &ext_cell_creator); + static td::Result load(td::Slice hash, td::Slice value, bool need_data, ExtCellCreator &ext_cell_creator); + td::Result load_refcnt(td::Slice hash); // This only loads refcnt_, cell_ == null private: std::shared_ptr reader_; @@ -60,6 +62,7 @@ class CellStorer { CellStorer(KeyValue &kv); td::Status erase(td::Slice hash); td::Status set(td::int32 refcnt, const td::Ref &cell, bool as_boc); + static std::string serialize_value(td::int32 refcnt, const td::Ref &cell, bool as_boc); private: KeyValue &kv_; diff --git a/crypto/vm/db/DynamicBagOfCellsDb.cpp b/crypto/vm/db/DynamicBagOfCellsDb.cpp index 1aa4e0f56..b69cd8c0b 100644 --- a/crypto/vm/db/DynamicBagOfCellsDb.cpp +++ b/crypto/vm/db/DynamicBagOfCellsDb.cpp @@ -27,6 +27,9 @@ #include "td/utils/ThreadSafeCounter.h" #include "vm/cellslice.h" +#include +#include "td/actor/actor.h" +#include "common/delay.h" namespace vm { namespace { @@ -60,6 +63,20 @@ struct CellInfo { bool operator<(const CellInfo &other) const { return key() < other.key(); } + + struct Eq { + using is_transparent = void; // Pred to use + bool operator()(const CellInfo &info, const CellInfo &other_info) const { return info.key() == other_info.key();} + bool operator()(const CellInfo &info, td::Slice hash) const { return info.key().as_slice() == hash;} + bool operator()(td::Slice hash, const CellInfo &info) const { return info.key().as_slice() == hash;} + + }; + struct Hash { + using is_transparent = void; // Pred to use + using transparent_key_equal = Eq; + size_t operator()(td::Slice hash) const { return cell_hash_slice_hash(hash); } + size_t operator()(const CellInfo &info) const { return cell_hash_slice_hash(info.key().as_slice());} + }; }; bool operator<(const CellInfo &a, td::Slice b) { @@ -86,6 +103,12 @@ class DynamicBagOfCellsDbImpl : public DynamicBagOfCellsDb, private ExtCellCreat TRY_RESULT(loaded_cell, get_cell_info_force(hash).cell->load_cell()); return std::move(loaded_cell.data_cell); } + td::Result> load_root(td::Slice hash) override { + return load_cell(hash); + } + td::Result> load_root_thread_safe(td::Slice hash) const override { + return td::Status::Error("Not implemented"); + } void load_cell_async(td::Slice hash, std::shared_ptr executor, td::Promise> promise) override { auto info = hash_table_.get_if_exists(hash); @@ -160,6 +183,9 @@ class DynamicBagOfCellsDbImpl : public DynamicBagOfCellsDb, private ExtCellCreat } td::Status prepare_commit() override { + if (pca_state_) { + return td::Status::Error("prepare_commit_async is not finished"); + } if (is_prepared_for_commit()) { return td::Status::OK(); } @@ -565,6 +591,221 @@ class DynamicBagOfCellsDbImpl : public DynamicBagOfCellsDb, private ExtCellCreat DynamicBocExtCellExtra{cell_db_reader_})); return std::move(res); } + + struct PrepareCommitAsyncState { + size_t remaining_ = 0; + std::shared_ptr executor_; + td::Promise promise_; + + struct CellInfo2 { + CellInfo *info{}; + std::vector parents; + unsigned remaining_children = 0; + Cell::Hash key() const { + return info->key(); + } + bool operator<(const CellInfo2 &other) const { + return key() < other.key(); + } + + friend bool operator<(const CellInfo2 &a, td::Slice b) { + return a.key().as_slice() < b; + } + + friend bool operator<(td::Slice a, const CellInfo2 &b) { + return a < b.key().as_slice(); + } + + struct Eq { + using is_transparent = void; // Pred to use + bool operator()(const CellInfo2 &info, const CellInfo2 &other_info) const { + return info.key() == other_info.key(); + } + bool operator()(const CellInfo2 &info, td::Slice hash) const { + return info.key().as_slice() == hash; + } + bool operator()(td::Slice hash, const CellInfo2 &info) const { + return info.key().as_slice() == hash; + } + }; + struct Hash { + using is_transparent = void; // Pred to use + using transparent_key_equal = Eq; + size_t operator()(td::Slice hash) const { + return cell_hash_slice_hash(hash); + } + size_t operator()(const CellInfo2 &info) const { + return cell_hash_slice_hash(info.key().as_slice()); + } + }; + }; + + CellHashTable cells_; + + std::queue load_queue_; + td::uint32 active_load_ = 0; + td::uint32 max_parallel_load_ = 4; + }; + std::unique_ptr pca_state_; + + void prepare_commit_async(std::shared_ptr executor, td::Promise promise) override { + hash_table_ = {}; + if (pca_state_) { + promise.set_error(td::Status::Error("Other prepare_commit_async is not finished")); + return; + } + if (is_prepared_for_commit()) { + promise.set_result(td::Unit()); + return; + } + pca_state_ = std::make_unique(); + pca_state_->executor_ = std::move(executor); + pca_state_->promise_ = std::move(promise); + for (auto &new_cell : to_inc_) { + dfs_new_cells_in_db_async(new_cell); + } + pca_state_->cells_.for_each([&](PrepareCommitAsyncState::CellInfo2 &info) { + ++pca_state_->remaining_; + if (info.remaining_children == 0) { + pca_load_from_db(&info); + } + }); + if (pca_state_->remaining_ == 0) { + prepare_commit_async_cont(); + } + } + + void dfs_new_cells_in_db_async(const td::Ref &cell, PrepareCommitAsyncState::CellInfo2 *parent = nullptr) { + bool exists = true; + pca_state_->cells_.apply(cell->get_hash().as_slice(), [&](PrepareCommitAsyncState::CellInfo2 &info) { + if (info.info == nullptr) { + exists = false; + info.info = &get_cell_info(cell); + } + }); + auto info = pca_state_->cells_.get_if_exists(cell->get_hash().as_slice()); + if (parent) { + info->parents.push_back(parent); + ++parent->remaining_children; + } + if (exists) { + return; + } + if (cell->is_loaded()) { + vm::CellSlice cs(vm::NoVm{}, cell); + for (unsigned i = 0; i < cs.size_refs(); i++) { + dfs_new_cells_in_db_async(cs.prefetch_ref(i), info); + } + } + } + + void pca_load_from_db(PrepareCommitAsyncState::CellInfo2 *info) { + if (pca_state_->active_load_ >= pca_state_->max_parallel_load_) { + pca_state_->load_queue_.push(info); + return; + } + ++pca_state_->active_load_; + pca_state_->executor_->execute_async( + [db = this, info, executor = pca_state_->executor_, loader = *loader_]() mutable { + auto res = loader.load_refcnt(info->info->cell->get_hash().as_slice()).move_as_ok(); + executor->execute_sync([db, info, res = std::move(res)]() { + --db->pca_state_->active_load_; + db->pca_process_load_queue(); + db->pca_set_in_db(info, std::move(res)); + }); + }); + } + + void pca_process_load_queue() { + while (pca_state_->active_load_ < pca_state_->max_parallel_load_ && !pca_state_->load_queue_.empty()) { + PrepareCommitAsyncState::CellInfo2 *info = pca_state_->load_queue_.front(); + pca_state_->load_queue_.pop(); + pca_load_from_db(info); + } + } + + void pca_set_in_db(PrepareCommitAsyncState::CellInfo2 *info, CellLoader::LoadResult result) { + info->info->sync_with_db = true; + if (result.status == CellLoader::LoadResult::Ok) { + info->info->in_db = true; + info->info->db_refcnt = result.refcnt(); + } else { + info->info->in_db = false; + } + for (PrepareCommitAsyncState::CellInfo2 *parent_info : info->parents) { + if (parent_info->info->sync_with_db) { + continue; + } + if (!info->info->in_db) { + pca_set_in_db(parent_info, {}); + } else if (--parent_info->remaining_children == 0) { + pca_load_from_db(parent_info); + } + } + CHECK(pca_state_->remaining_ != 0); + if (--pca_state_->remaining_ == 0) { + prepare_commit_async_cont(); + } + } + + void prepare_commit_async_cont() { + for (auto &new_cell : to_inc_) { + auto &new_cell_info = get_cell_info(new_cell); + dfs_new_cells(new_cell_info); + } + + CHECK(pca_state_->remaining_ == 0); + for (auto &old_cell : to_dec_) { + auto &old_cell_info = get_cell_info(old_cell); + dfs_old_cells_async(old_cell_info); + } + if (pca_state_->remaining_ == 0) { + prepare_commit_async_cont2(); + } + } + + void dfs_old_cells_async(CellInfo &info) { + if (!info.was) { + info.was = true; + visited_.push_back(&info); + if (!info.sync_with_db) { + ++pca_state_->remaining_; + load_cell_async( + info.cell->get_hash().as_slice(), pca_state_->executor_, + [executor = pca_state_->executor_, db = this, info = &info](td::Result> R) { + R.ensure(); + executor->execute_sync([db, info]() { + CHECK(info->sync_with_db); + db->dfs_old_cells_async(*info); + if (--db->pca_state_->remaining_ == 0) { + db->prepare_commit_async_cont2(); + } + }); + }); + return; + } + } + info.refcnt_diff--; + if (!info.sync_with_db) { + return; + } + auto new_refcnt = info.refcnt_diff + info.db_refcnt; + CHECK(new_refcnt >= 0); + if (new_refcnt != 0) { + return; + } + + for_each(info, [this](auto &child_info) { dfs_old_cells_async(child_info); }); + } + + void prepare_commit_async_cont2() { + save_diff_prepare(); + to_inc_.clear(); + to_dec_.clear(); + pca_state_->promise_.set_result(td::Unit()); + pca_state_ = {}; + } + }; } // namespace diff --git a/crypto/vm/db/DynamicBagOfCellsDb.h b/crypto/vm/db/DynamicBagOfCellsDb.h index fa2b44d21..62864ad97 100644 --- a/crypto/vm/db/DynamicBagOfCellsDb.h +++ b/crypto/vm/db/DynamicBagOfCellsDb.h @@ -23,6 +23,11 @@ #include "td/utils/Status.h" #include "td/actor/PromiseFuture.h" +#include + +namespace td { +class KeyValueReader; +} namespace vm { class CellLoader; class CellStorer; @@ -45,12 +50,20 @@ class DynamicBagOfCellsDb { public: virtual ~DynamicBagOfCellsDb() = default; virtual td::Result> load_cell(td::Slice hash) = 0; + virtual td::Result> load_root(td::Slice hash) = 0; + virtual td::Result> load_root_thread_safe(td::Slice hash) const = 0; struct Stats { + td::int64 roots_total_count{0}; td::int64 cells_total_count{0}; td::int64 cells_total_size{0}; - void apply_diff(Stats diff) { + std::vector> custom_stats; + void apply_diff(const Stats &diff) { + roots_total_count += diff.roots_total_count; cells_total_count += diff.cells_total_count; cells_total_size += diff.cells_total_size; + CHECK(roots_total_count >= 0); + CHECK(cells_total_count >= 0); + CHECK(cells_total_size >= 0); } }; virtual void inc(const Ref &old_root) = 0; @@ -58,6 +71,9 @@ class DynamicBagOfCellsDb { virtual td::Status prepare_commit() = 0; virtual Stats get_stats_diff() = 0; + virtual td::Result get_stats() { + return td::Status::Error("Not implemented"); + } virtual td::Status commit(CellStorer &) = 0; virtual std::shared_ptr get_cell_db_reader() = 0; @@ -65,19 +81,31 @@ class DynamicBagOfCellsDb { virtual td::Status set_loader(std::unique_ptr loader) = 0; virtual void set_celldb_compress_depth(td::uint32 value) = 0; - virtual vm::ExtCellCreator& as_ext_cell_creator() = 0; + virtual vm::ExtCellCreator &as_ext_cell_creator() = 0; static std::unique_ptr create(); + struct CreateInMemoryOptions { + size_t extra_threads{std::thread::hardware_concurrency()}; + bool verbose{true}; + // Allocated DataCels will never be deleted + bool use_arena{false}; + // Almost no overhead in memory during creation, but will scan database twice + bool use_less_memory_during_creation{true}; + }; + static std::unique_ptr create_in_memory(td::KeyValueReader *kv, CreateInMemoryOptions options); + class AsyncExecutor { public: - virtual ~AsyncExecutor() {} + virtual ~AsyncExecutor() { + } virtual void execute_async(std::function f) = 0; virtual void execute_sync(std::function f) = 0; }; virtual void load_cell_async(td::Slice hash, std::shared_ptr executor, td::Promise> promise) = 0; + virtual void prepare_commit_async(std::shared_ptr executor, td::Promise promise) = 0; }; } // namespace vm diff --git a/crypto/vm/db/InMemoryBagOfCellsDb.cpp b/crypto/vm/db/InMemoryBagOfCellsDb.cpp new file mode 100644 index 000000000..03cad0934 --- /dev/null +++ b/crypto/vm/db/InMemoryBagOfCellsDb.cpp @@ -0,0 +1,988 @@ +#include "CellStorage.h" +#include "DynamicBagOfCellsDb.h" +#include "td/utils/Timer.h" +#include "td/utils/base64.h" +#include "td/utils/format.h" +#include "td/utils/int_types.h" +#include "td/utils/misc.h" +#include "td/utils/port/Stat.h" +#include "vm/cells/CellHash.h" +#include "vm/cells/CellSlice.h" +#include "vm/cells/DataCell.h" +#include "vm/cells/ExtCell.h" + +#include "td/utils/HashMap.h" +#include "td/utils/HashSet.h" + +#include + +#if TD_PORT_POSIX +#include +#include +#endif + +namespace vm { +namespace { +constexpr bool use_dense_hash_map = true; + +template +void parallel_run(size_t n, F &&run_task, size_t extra_threads_n) { + std::atomic next_task_id{0}; + auto loop = [&] { + while (true) { + auto task_id = next_task_id++; + if (task_id >= n) { + break; + } + run_task(task_id); + } + }; + + // NB: it could be important that td::thread is used, not std::thread + std::vector threads; + for (size_t i = 0; i < extra_threads_n; i++) { + threads.emplace_back(loop); + } + loop(); + for (auto &thread : threads) { + thread.join(); + } + threads.clear(); +} + +struct UniqueAccess { + struct Release { + void operator()(UniqueAccess *access) const { + if (access) { + access->release(); + } + } + }; + using Lock = std::unique_ptr; + Lock lock() { + CHECK(!locked_.exchange(true)); + return Lock(this); + } + + private: + std::atomic locked_{false}; + void release() { + locked_ = false; + } +}; +class DefaultPrunnedCellCreator : public ExtCellCreator { + public: + td::Result> ext_cell(Cell::LevelMask level_mask, td::Slice hash, td::Slice depth) override { + TRY_RESULT(cell, PrunnedCell::create(PrunnedCellInfo{level_mask, hash, depth}, td::Unit{})); + return cell; + } +}; + +class ArenaPrunnedCellCreator : public ExtCellCreator { + struct ArenaAllocator { + ArenaAllocator() { + // only one instance ever + static UniqueAccess unique_access; + [[maybe_unused]] auto ptr = unique_access.lock().release(); + } + std::mutex mutex; + struct Deleter { + static constexpr size_t batch_size = 1 << 24; +#if TD_PORT_POSIX + static std::unique_ptr alloc() { + char *ptr = reinterpret_cast( + mmap(NULL, batch_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); + CHECK(ptr != nullptr); + return std::unique_ptr(ptr); + } + void operator()(char *ptr) const { + munmap(ptr, batch_size); + } +#else + static std::unique_ptr alloc() { + auto ptr = reinterpret_cast(malloc(batch_size)); + CHECK(ptr != nullptr); + return std::unique_ptr(ptr); + } + void operator()(char *ptr) const { + free(ptr); + } +#endif + }; + std::vector> arena; + td::uint64 arena_generation{0}; + + td::MutableSlice alloc_batch() { + auto batch = Deleter::alloc(); + auto res = td::MutableSlice(batch.get(), Deleter::batch_size); + std::lock_guard guard(mutex); + arena.emplace_back(std::move(batch)); + return res; + } + + char *alloc(size_t size) { + thread_local td::MutableSlice batch; + thread_local td::uint64 batch_generation{0}; + auto aligned_size = (size + 7) / 8 * 8; + if (batch.size() < size || batch_generation != arena_generation) { + batch = alloc_batch(); + batch_generation = arena_generation; + } + auto res = batch.begin(); + batch.remove_prefix(aligned_size); + return res; + } + void clear() { + std::lock_guard guard(mutex); + arena_generation++; + td::reset_to_empty(arena); + } + }; + static ArenaAllocator arena_; + static td::ThreadSafeCounter cells_count_; + + public: + struct Counter { + Counter() { + cells_count_.add(1); + } + Counter(Counter &&other) { + cells_count_.add(1); + } + Counter(const Counter &other) { + cells_count_.add(1); + } + ~Counter() { + cells_count_.add(-1); + } + }; + + struct Allocator { + template + std::unique_ptr> make_unique(ArgsT &&...args) { + auto *ptr = arena_.alloc(sizeof(T)); + T *obj = new (ptr) T(std::forward(args)...); + return std::unique_ptr(obj); + } + }; + td::Result> ext_cell(Cell::LevelMask level_mask, td::Slice hash, td::Slice depth) override { + Allocator allocator; + TRY_RESULT(cell, PrunnedCell::create(allocator, PrunnedCellInfo{level_mask, hash, depth}, Counter())); + return cell; + } + static td::int64 count() { + return cells_count_.sum(); + } + static void clear_arena() { + LOG_CHECK(cells_count_.sum() == 0) << cells_count_.sum(); + arena_.clear(); + } +}; +td::ThreadSafeCounter ArenaPrunnedCellCreator::cells_count_; +ArenaPrunnedCellCreator::ArenaAllocator ArenaPrunnedCellCreator::arena_; + +struct CellInfo { + mutable td::int32 db_refcnt{0}; + Ref cell; +}; +static_assert(sizeof(CellInfo) == 16); + +CellHash as_cell_hash(const CellInfo &info) { + return info.cell->get_hash(); +} + +struct CellInfoHashTableBaseline { + td::HashSet ht_; + const CellInfo *find(CellHash hash) const { + if (auto it = ht_.find(hash); it != ht_.end()) { + return &*it; + } + return nullptr; + } + void erase(CellHash hash) { + auto it = ht_.find(hash); + CHECK(it != ht_.end()); + ht_.erase(it); + } + void insert(CellInfo info) { + ht_.insert(std::move(info)); + } + template + void init_from(Iterator begin, Iterator end) { + ht_ = td::HashSet(begin, end); + } + size_t size() const { + return ht_.size(); + } + auto begin() const { + return ht_.begin(); + } + auto end() const { + return ht_.end(); + } + size_t bucket_count() const { + return ht_.bucket_count(); + } + template + auto for_each(F &&f) { + for (auto &it : ht_) { + f(it); + } + } +}; + +struct CellInfoHashTableDense { + size_t dense_ht_size_{0}; + size_t dense_ht_buckets_{1}; + std::vector dense_ht_offsets_{1}; + std::vector dense_ht_values_; + td::HashSet new_ht_; + size_t dense_choose_bucket(const CellHash &hash) const { + return cell_hash_slice_hash(hash.as_slice()) % dense_ht_buckets_; + } + const CellInfo *dense_find(CellHash hash) const { + auto bucket_i = dense_choose_bucket(hash); + auto begin = dense_ht_values_.begin() + dense_ht_offsets_[bucket_i]; + auto end = dense_ht_values_.begin() + dense_ht_offsets_[bucket_i + 1]; + for (auto it = begin; it != end; ++it) { + if (it->cell.not_null() && it->cell->get_hash() == hash) { + return &*it; + } + } + return nullptr; + } + CellInfo *dense_find_empty(CellHash hash) { + auto bucket_i = dense_choose_bucket(hash); + auto begin = dense_ht_values_.begin() + dense_ht_offsets_[bucket_i]; + auto end = dense_ht_values_.begin() + dense_ht_offsets_[bucket_i + 1]; + for (auto it = begin; it != end; ++it) { + if (it->cell.is_null()) { + return &*it; + } + } + return nullptr; + } + const CellInfo *find(CellHash hash) const { + if (auto it = new_ht_.find(hash); it != new_ht_.end()) { + return &*it; + } + if (auto it = dense_find(hash)) { + return it; + } + return nullptr; + } + void erase(CellHash hash) { + if (auto it = new_ht_.find(hash); it != new_ht_.end()) { + new_ht_.erase(it); + return; + } + auto info = dense_find(hash); + CHECK(info && info->db_refcnt > 0); + info->db_refcnt = 0; + const_cast(info)->cell = {}; + CHECK(dense_ht_size_ > 0); + dense_ht_size_--; + } + + void insert(CellInfo info) { + if (auto dest = dense_find_empty(info.cell->get_hash())) { + *dest = std::move(info); + dense_ht_size_++; + return; + } + new_ht_.insert(std::move(info)); + } + template + void init_from(Iterator begin, Iterator end) { + auto size = td::narrow_cast(std::distance(begin, end)); + dense_ht_buckets_ = std::max(size_t(1), size_t(size / 8)); + + std::vector offsets(dense_ht_buckets_ + 2); + for (auto it = begin; it != end; ++it) { + auto bucket_i = dense_choose_bucket(it->cell->get_hash()); + offsets[bucket_i + 2]++; + } + for (size_t i = 1; i < offsets.size(); i++) { + offsets[i] += offsets[i - 1]; + } + dense_ht_values_.resize(size); + for (auto it = begin; it != end; ++it) { + auto bucket_i = dense_choose_bucket(it->cell->get_hash()); + dense_ht_values_[offsets[bucket_i + 1]++] = std::move(*it); + } + CHECK(offsets[0] == 0); + CHECK(offsets[offsets.size() - 1] == size); + CHECK(offsets[offsets.size() - 2] == size); + dense_ht_offsets_ = std::move(offsets); + dense_ht_size_ = size; + } + size_t size() const { + return dense_ht_size_ + new_ht_.size(); + } + template + auto for_each(F &&f) { + for (auto &it : dense_ht_values_) { + if (it.cell.not_null()) { + f(it); + } + } + for (auto &it : new_ht_) { + f(it); + } + } + size_t bucket_count() const { + return new_ht_.bucket_count() + dense_ht_values_.size(); + } +}; + +using CellInfoHashTable = std::conditional_t; + +class CellStorage { + struct PrivateTag {}; + struct CellBucket; + struct None { + void operator()(CellBucket *bucket) { + } + }; + struct CellBucketRef { + UniqueAccess::Lock lock; + std::unique_ptr bucket; + CellBucket &operator*() { + return *bucket; + } + CellBucket *operator->() { + return bucket.get(); + } + }; + struct CellBucket { + mutable UniqueAccess access_; + CellInfoHashTable infos_; + std::vector cells_; + std::vector> roots_; + size_t boc_count_{0}; + [[maybe_unused]] char pad3[TD_CONCURRENCY_PAD]; + + void clear() { + td::reset_to_empty(infos_); + td::reset_to_empty(cells_); + td::reset_to_empty(roots_); + } + + CellBucketRef unique_access() const { + auto lock = access_.lock(); + return CellBucketRef{.lock = std::move(lock), + .bucket = std::unique_ptr(const_cast(this))}; + } + }; + std::array buckets_{}; + bool inited_{false}; + + const CellBucket &get_bucket(size_t i) const { + return buckets_.at(i); + } + const CellBucket &get_bucket(const CellHash &hash) const { + return get_bucket(hash.as_array()[0]); + } + + mutable UniqueAccess local_access_; + td::HashSet, CellHashF, CellEqF> local_roots_; + DynamicBagOfCellsDb::Stats stats_; + + mutable std::mutex root_mutex_; + td::HashSet, CellHashF, CellEqF> roots_; + + public: + std::optional get_info(const CellHash &hash) const { + auto lock = local_access_.lock(); + auto &bucket = get_bucket(hash); + if (auto info_ptr = bucket.infos_.find(hash)) { + return *info_ptr; + } + return {}; + } + + DynamicBagOfCellsDb::Stats get_stats() { + auto unique_access = local_access_.lock(); + auto stats = stats_; + auto add_stat = [&stats](auto key, auto value) { + stats.custom_stats.emplace_back(std::move(key), PSTRING() << value); + }; + if constexpr (use_dense_hash_map) { + size_t dense_ht_capacity = 0; + size_t new_ht_capacity = 0; + size_t dense_ht_size = 0; + size_t new_ht_size = 0; + for_each_bucket(0, [&](auto bucket_id, CellBucket &bucket) { + dense_ht_capacity += bucket.infos_.dense_ht_values_.size(); + dense_ht_size += bucket.infos_.dense_ht_size_; + new_ht_capacity += bucket.infos_.new_ht_.bucket_count(); + new_ht_size += bucket.infos_.new_ht_.size(); + }); + auto size = new_ht_size + dense_ht_size; + auto capacity = new_ht_capacity + dense_ht_capacity; + add_stat("ht.capacity", capacity); + add_stat("ht.size", size); + add_stat("ht.load", double(size) / std::max(1.0, double(capacity))); + add_stat("ht.dense_ht_capacity", dense_ht_capacity); + add_stat("ht.dense_ht_size", dense_ht_size); + add_stat("ht.dense_ht_load", double(dense_ht_size) / std::max(1.0, double(dense_ht_capacity))); + add_stat("ht.new_ht_capacity", new_ht_capacity); + add_stat("ht.new_ht_size", new_ht_size); + add_stat("ht.new_ht_load", double(new_ht_size) / std::max(1.0, double(new_ht_capacity))); + } else { + size_t capacity = 0; + size_t size = 0; + for_each_bucket(0, [&](auto bucket_id, CellBucket &bucket) { + capacity += bucket.infos_.bucket_count(); + size += bucket.infos_.size(); + }); + add_stat("ht.capacity", capacity); + add_stat("ht.size", size); + add_stat("ht.load", double(size) / std::max(1.0, double(capacity))); + } + CHECK(td::narrow_cast(stats.roots_total_count) == local_roots_.size()); + return stats; + } + void apply_stats_diff(DynamicBagOfCellsDb::Stats diff) { + auto unique_access = local_access_.lock(); + stats_.apply_diff(diff); + CHECK(td::narrow_cast(stats_.roots_total_count) == local_roots_.size()); + size_t cells_count{0}; + for_each_bucket(0, [&](size_t bucket_id, auto &bucket) { cells_count += bucket.infos_.size(); }); + CHECK(td::narrow_cast(stats_.cells_total_count) == cells_count); + } + + td::Result> load_cell(const CellHash &hash) const { + auto lock = local_access_.lock(); + auto &bucket = get_bucket(hash); + if (auto info_ptr = bucket.infos_.find(hash)) { + return info_ptr->cell; + } + return td::Status::Error("not found"); + } + + td::Result> load_root_local(const CellHash &hash) const { + auto lock = local_access_.lock(); + if (auto it = local_roots_.find(hash); it != local_roots_.end()) { + return *it; + } + return td::Status::Error("not found"); + } + td::Result> load_root_shared(const CellHash &hash) const { + std::lock_guard lock(root_mutex_); + if (auto it = roots_.find(hash); it != roots_.end()) { + return *it; + } + return td::Status::Error("not found"); + } + + void erase(const CellHash &hash) { + auto lock = local_access_.lock(); + auto bucket = get_bucket(hash).unique_access(); + bucket->infos_.erase(hash); + if (auto local_it = local_roots_.find(hash); local_it != local_roots_.end()) { + local_roots_.erase(local_it); + std::lock_guard root_lock(root_mutex_); + auto shared_it = roots_.find(hash); + CHECK(shared_it != roots_.end()); + roots_.erase(shared_it); + CHECK(stats_.roots_total_count > 0); + stats_.roots_total_count--; + } + } + + void add_new_root(Ref cell) { + auto lock = local_access_.lock(); + if (local_roots_.insert(cell).second) { + std::lock_guard lock(root_mutex_); + roots_.insert(std::move(cell)); + stats_.roots_total_count++; + } + } + + void set(td::int32 refcnt, Ref cell) { + auto lock = local_access_.lock(); + //LOG(ERROR) << "setting refcnt to " << refcnt << ", cell " << td::base64_encode(cell->get_hash().as_slice()); + auto hash = cell->get_hash(); + auto bucket = get_bucket(hash).unique_access(); + if (auto info_ptr = bucket->infos_.find(hash)) { + CHECK(info_ptr->cell.get() == cell.get()); + info_ptr->db_refcnt = refcnt; + } else { + bucket->infos_.insert({.db_refcnt = refcnt, .cell = std::move(cell)}); + } + } + + template + static td::unique_ptr build(DynamicBagOfCellsDb::CreateInMemoryOptions options, + F &¶llel_scan_cells) { + auto storage = td::make_unique(PrivateTag{}); + storage->do_build(options, parallel_scan_cells); + return storage; + } + + ~CellStorage() { + clear(); + } + CellStorage() = delete; + explicit CellStorage(PrivateTag) { + } + + private: + template + void do_build(DynamicBagOfCellsDb::CreateInMemoryOptions options, F &¶llel_scan_cells) { + auto verbose = options.verbose; + td::Slice P = "loading in-memory cell database: "; + LOG_IF(WARNING, verbose) << P << "start with options use_arena=" << options.use_arena + << " use_less_memory_during_creation=" << options.use_less_memory_during_creation + << " use_dense_hash_map=" << use_dense_hash_map; + auto full_timer = td::Timer(); + auto lock = local_access_.lock(); + CHECK(ArenaPrunnedCellCreator::count() == 0); + ArenaPrunnedCellCreator arena_pc_creator; + DefaultPrunnedCellCreator default_pc_creator; + + auto timer = td::Timer(); + td::int64 cell_count{0}; + td::int64 desc_count{0}; + if (options.use_less_memory_during_creation) { + auto [new_cell_count, new_desc_count] = parallel_scan_cells( + default_pc_creator, options.use_arena, + [&](td::int32 refcnt, Ref cell) { initial_set_without_refs(refcnt, std::move(cell)); }); + cell_count = new_cell_count; + desc_count = new_desc_count; + } else { + auto [new_cell_count, new_desc_count] = + parallel_scan_cells(arena_pc_creator, options.use_arena, + [&](td::int32 refcnt, Ref cell) { initial_set(refcnt, std::move(cell)); }); + cell_count = new_cell_count; + desc_count = new_desc_count; + } + LOG_IF(WARNING, verbose) << P << "cells loaded in " << timer.elapsed() << "s, cells_count= " << cell_count + << " prunned_cells_count=" << ArenaPrunnedCellCreator::count(); + + timer = td::Timer(); + for_each_bucket(options.extra_threads, [&](size_t bucket_id, auto &bucket) { build_hashtable(bucket); }); + + size_t ht_capacity = 0; + size_t ht_size = 0; + for_each_bucket(0, [&](size_t bucket_id, auto &bucket) { + ht_size += bucket.infos_.size(); + ht_capacity += bucket.infos_.bucket_count(); + }); + double load_factor = double(ht_size) / std::max(double(ht_capacity), 1.0); + LOG_IF(WARNING, verbose) << P << "hashtable created in " << timer.elapsed() + << "s, hashtables_expected_size=" << td::format::as_size(ht_capacity * sizeof(CellInfo)) + << " load_factor=" << load_factor; + + timer = td::Timer(); + if (options.use_less_memory_during_creation) { + auto [new_cell_count, new_desc_count] = + parallel_scan_cells(default_pc_creator, false, + [&](td::int32 refcnt, Ref cell) { secondary_set(refcnt, std::move(cell)); }); + CHECK(new_cell_count == cell_count); + CHECK(new_desc_count == desc_count); + } else { + for_each_bucket(options.extra_threads, [&](size_t bucket_id, auto &bucket) { reset_refs(bucket); }); + } + LOG_IF(WARNING, verbose) << P << "refs rearranged in " << timer.elapsed() << "s"; + + timer = td::Timer(); + using Stats = DynamicBagOfCellsDb::Stats; + std::vector bucket_stats(buckets_.size()); + std::atomic boc_count{0}; + for_each_bucket(options.extra_threads, [&](size_t bucket_id, auto &bucket) { + bucket_stats[bucket_id] = validate_bucket_a(bucket, options.use_arena); + boc_count += bucket.boc_count_; + }); + for_each_bucket(options.extra_threads, [&](size_t bucket_id, auto &bucket) { validate_bucket_b(bucket); }); + stats_ = {}; + for (auto &bucket_stat : bucket_stats) { + stats_.apply_diff(bucket_stat); + } + LOG_IF(WARNING, verbose) << P << "refcnt validated in " << timer.elapsed() << "s"; + + timer = td::Timer(); + build_roots(); + LOG_IF(WARNING, verbose) << P << "roots hashtable built in " << timer.elapsed() << "s"; + ArenaPrunnedCellCreator::clear_arena(); + LOG_IF(WARNING, verbose) << P << "arena cleared in " << timer.elapsed(); + + lock.reset(); + auto r_mem_stat = td::mem_stat(); + td::MemStat mem_stat; + if (r_mem_stat.is_ok()) { + mem_stat = r_mem_stat.move_as_ok(); + } + auto stats = get_stats(); + td::StringBuilder sb; + for (auto &[key, value] : stats.custom_stats) { + sb << "\n\t" << key << "=" << value; + } + LOG_IF(ERROR, desc_count != 0 && desc_count != stats.roots_total_count + 1) + << "desc<> keys count is " << desc_count << " wich is different from roots count " << stats.roots_total_count; + LOG_IF(WARNING, verbose) + << P << "done in " << full_timer.elapsed() << "\n\troots_count=" << stats.roots_total_count << "\n\t" + << desc_count << "\n\tcells_count=" << stats.cells_total_count + << "\n\tcells_size=" << td::format::as_size(stats.cells_total_size) << "\n\tboc_count=" << boc_count.load() + << sb.as_cslice() << "\n\tdata_cells_size=" << td::format::as_size(sizeof(DataCell) * stats.cells_total_count) + << "\n\tdata_cell_size=" << sizeof(DataCell) << "\n\texpected_memory_used=" + << td::format::as_size(stats.cells_total_count * (sizeof(DataCell) + sizeof(CellInfo) * 3 / 2) + + stats.cells_total_size) + << "\n\tbest_possible_memory_used" + << td::format::as_size(stats.cells_total_count * (sizeof(DataCell) + sizeof(CellInfo)) + stats.cells_total_size) + << "\n\tmemory_used=" << td::format::as_size(mem_stat.resident_size_) + << "\n\tpeak_memory_used=" << td::format::as_size(mem_stat.resident_size_peak_); + + inited_ = true; + } + + template + void for_each_bucket(size_t extra_threads, F &&f) { + parallel_run( + buckets_.size(), [&](auto task_id) { f(task_id, *get_bucket(task_id).unique_access()); }, extra_threads); + } + + void clear() { + auto unique_access = local_access_.lock(); + for_each_bucket(td::thread::hardware_concurrency(), [&](size_t bucket_id, auto &bucket) { bucket.clear(); }); + local_roots_.clear(); + { + auto lock = std::lock_guard(root_mutex_); + roots_.clear(); + } + } + + void initial_set(td::int32 refcnt, Ref cell) { + CHECK(!inited_); + auto bucket = get_bucket(cell->get_hash()).unique_access(); + bucket->cells_.push_back({.db_refcnt = refcnt, .cell = std::move(cell)}); + } + + void initial_set_without_refs(td::int32 refcnt, Ref cell_ref) { + CHECK(!inited_); + auto bucket = get_bucket(cell_ref->get_hash()).unique_access(); + auto &cell = const_cast(*cell_ref); + for (unsigned i = 0; i < cell.size_refs(); i++) { + auto to_destroy = cell.reset_ref_unsafe(i, Ref(), false); + if (to_destroy->is_loaded()) { + bucket->boc_count_++; + } + } + bucket->cells_.push_back({.db_refcnt = refcnt, .cell = std::move(cell_ref)}); + } + + void secondary_set(td::int32 refcnt, Ref cell_copy) { + CHECK(!inited_); + auto bucket = get_bucket(cell_copy->get_hash()).unique_access(); + auto info = bucket->infos_.find(cell_copy->get_hash()); + CHECK(info); + CellSlice cs(NoVm{}, std::move(cell_copy)); + auto &cell = const_cast(*info->cell); + CHECK(cs.size_refs() == cell.size_refs()); + for (unsigned i = 0; i < cell.size_refs(); i++) { + auto prunned_cell_hash = cs.fetch_ref()->get_hash(); + auto &prunned_cell_bucket = get_bucket(prunned_cell_hash); + auto full_cell_ptr = prunned_cell_bucket.infos_.find(prunned_cell_hash); + CHECK(full_cell_ptr); + auto full_cell = full_cell_ptr->cell; + auto to_destroy = cell.reset_ref_unsafe(i, std::move(full_cell), false); + CHECK(to_destroy.is_null()); + } + } + + void build_hashtable(CellBucket &bucket) { + bucket.infos_.init_from(bucket.cells_.begin(), bucket.cells_.end()); + LOG_CHECK(bucket.infos_.size() == bucket.cells_.size()) << bucket.infos_.size() << " vs " << bucket.cells_.size(); + td::reset_to_empty(bucket.cells_); + LOG_CHECK(bucket.cells_.capacity() == 0) << bucket.cells_.capacity(); + } + + void reset_refs(CellBucket &bucket) { + bucket.infos_.for_each([&](auto &it) { + // This is generally very dangerous, but should be safe here + auto &cell = const_cast(*it.cell); + for (unsigned i = 0; i < cell.size_refs(); i++) { + auto prunned_cell = cell.get_ref_raw_ptr(i); + auto prunned_cell_hash = prunned_cell->get_hash(); + auto &prunned_cell_bucket = get_bucket(prunned_cell_hash); + auto full_cell_ptr = prunned_cell_bucket.infos_.find(prunned_cell_hash); + CHECK(full_cell_ptr); + auto full_cell = full_cell_ptr->cell; + auto to_destroy = cell.reset_ref_unsafe(i, std::move(full_cell)); + if (!to_destroy->is_loaded()) { + Ref> x(std::move(to_destroy)); + x->~PrunnedCell(); + x.release(); + } else { + bucket.boc_count_++; + } + } + }); + } + + DynamicBagOfCellsDb::Stats validate_bucket_a(CellBucket &bucket, bool use_arena) { + DynamicBagOfCellsDb::Stats stats; + bucket.infos_.for_each([&](auto &it) { + int cell_ref_cnt = it.cell->get_refcnt(); + CHECK(it.db_refcnt + 1 + use_arena >= cell_ref_cnt); + auto extra_refcnt = it.db_refcnt + 1 + use_arena - cell_ref_cnt; + if (extra_refcnt != 0) { + bucket.roots_.push_back(it.cell); + stats.roots_total_count++; + } + stats.cells_total_count++; + stats.cells_total_size += static_cast(it.cell->get_storage_size()); + }); + return stats; + } + void validate_bucket_b(CellBucket &bucket) { + // sanity check + bucket.infos_.for_each([&](auto &it) { + CellSlice cs(NoVm{}, it.cell); + while (cs.have_refs()) { + CHECK(cs.fetch_ref().not_null()); + } + }); + } + void build_roots() { + for (auto &it : buckets_) { + for (auto &root : it.roots_) { + local_roots_.insert(std::move(root)); + } + td::reset_to_empty(it.roots_); + } + auto lock = std::lock_guard(root_mutex_); + roots_ = local_roots_; + } +}; + +class InMemoryBagOfCellsDb : public DynamicBagOfCellsDb { + public: + explicit InMemoryBagOfCellsDb(td::unique_ptr storage) : storage_(std::move(storage)) { + } + + td::Result> load_cell(td::Slice hash) override { + return storage_->load_cell(CellHash::from_slice(hash)); + } + + td::Result> load_root(td::Slice hash) override { + return storage_->load_root_local(CellHash::from_slice(hash)); + } + td::Result> load_root_thread_safe(td::Slice hash) const override { + return storage_->load_root_shared(CellHash::from_slice(hash)); + } + + void inc(const Ref &cell) override { + if (cell.is_null()) { + return; + } + if (cell->get_virtualization() != 0) { + return; + } + to_inc_.push_back(cell); + } + + void dec(const Ref &cell) override { + if (cell.is_null()) { + return; + } + if (cell->get_virtualization() != 0) { + return; + } + to_dec_.push_back(cell); + } + + td::Status commit(CellStorer &cell_storer) override { + if (!to_inc_.empty() || !to_dec_.empty()) { + TRY_STATUS(prepare_commit()); + } + + Stats diff; + CHECK(to_dec_.empty()); + for (auto &it : info_) { + auto &info = it.second; + if (info.diff_refcnt == 0) { + continue; + } + auto refcnt = td::narrow_cast(static_cast(info.db_refcnt) + info.diff_refcnt); + CHECK(refcnt >= 0); + if (refcnt > 0) { + cell_storer.set(refcnt, info.cell, false); + storage_->set(refcnt, info.cell); + if (info.db_refcnt == 0) { + diff.cells_total_count++; + diff.cells_total_size += static_cast(info.cell->get_storage_size()); + } + } else { + cell_storer.erase(info.cell->get_hash().as_slice()); + storage_->erase(info.cell->get_hash()); + diff.cells_total_count--; + diff.cells_total_size -= static_cast(info.cell->get_storage_size()); + } + } + storage_->apply_stats_diff(diff); + info_ = {}; + return td::Status::OK(); + } + + td::Result get_stats() override { + return storage_->get_stats(); + } + + // Not implemented or trivial or deprecated methods + td::Status set_loader(std::unique_ptr loader) override { + return td::Status::OK(); + } + + td::Status prepare_commit() override { + CHECK(info_.empty()); + for (auto &to_inc : to_inc_) { + auto new_root = do_inc(to_inc); + storage_->add_new_root(std::move(new_root)); + } + for (auto &to_dec : to_dec_) { + do_dec(to_dec); + } + to_dec_ = {}; + to_inc_ = {}; + return td::Status::OK(); + } + void prepare_commit_async(std::shared_ptr executor, td::Promise promise) override { + TRY_STATUS_PROMISE(promise, prepare_commit()); + promise.set_value(td::Unit()); + } + Stats get_stats_diff() override { + LOG(FATAL) << "Not implemented"; + return {}; + } + std::shared_ptr get_cell_db_reader() override { + return {}; + } + void set_celldb_compress_depth(td::uint32 value) override { + LOG(FATAL) << "Not implemented"; + } + ExtCellCreator &as_ext_cell_creator() override { + UNREACHABLE(); + } + void load_cell_async(td::Slice hash, std::shared_ptr executor, + td::Promise> promise) override { + LOG(FATAL) << "Not implemented"; + } + + private: + td::unique_ptr storage_; + + struct Info { + td::int32 db_refcnt{0}; + td::int32 diff_refcnt{0}; + Ref cell; + }; + td::HashMap info_; + + std::unique_ptr loader_; + std::vector> to_inc_; + std::vector> to_dec_; + + Ref do_inc(Ref cell) { + auto cell_hash = cell->get_hash(); + if (auto it = info_.find(cell_hash); it != info_.end()) { + CHECK(it->second.diff_refcnt != std::numeric_limits::max()); + it->second.diff_refcnt++; + return it->second.cell; + } + if (auto o_info = storage_->get_info(cell_hash)) { + info_.emplace(cell_hash, Info{.db_refcnt = o_info->db_refcnt, .diff_refcnt = 1, .cell = o_info->cell}); + return std::move(o_info->cell); + } + + CellSlice cs(NoVm{}, std::move(cell)); + CellBuilder cb; + cb.store_bits(cs.data(), cs.size()); + while (cs.have_refs()) { + auto ref = do_inc(cs.fetch_ref()); + cb.store_ref(std::move(ref)); + } + auto res = cb.finalize(cs.is_special()); + CHECK(res->get_hash() == cell_hash); + info_.emplace(cell_hash, Info{.db_refcnt = 0, .diff_refcnt = 1, .cell = res}); + return res; + } + + void do_dec(Ref cell) { + auto cell_hash = cell->get_hash(); + auto it = info_.find(cell_hash); + if (it != info_.end()) { + CHECK(it->second.diff_refcnt != std::numeric_limits::min()); + --it->second.diff_refcnt; + } else { + auto info = *storage_->get_info(cell_hash); + it = info_.emplace(cell_hash, Info{.db_refcnt = info.db_refcnt, .diff_refcnt = -1, .cell = info.cell}).first; + } + if (it->second.diff_refcnt + it->second.db_refcnt != 0) { + return; + } + CellSlice cs(NoVm{}, std::move(cell)); + while (cs.have_refs()) { + do_dec(cs.fetch_ref()); + } + } +}; + +} // namespace + +std::unique_ptr DynamicBagOfCellsDb::create_in_memory(td::KeyValueReader *kv, + CreateInMemoryOptions options) { + if (kv == nullptr) { + LOG_IF(WARNING, options.verbose) << "Create empty in-memory cells database (no key value is given)"; + auto storage = CellStorage::build(options, [](auto, auto, auto) { return std::make_pair(0, 0); }); + return std::make_unique(std::move(storage)); + } + + std::vector keys; + keys.emplace_back(""); + for (td::uint32 c = 1; c <= 0xff; c++) { + keys.emplace_back(1, static_cast(c)); + } + keys.emplace_back(33, static_cast(0xff)); + + auto parallel_scan_cells = [&](ExtCellCreator &pc_creator, bool use_arena, + auto &&f) -> std::pair { + std::atomic cell_count{0}; + std::atomic desc_count{0}; + parallel_run( + keys.size() - 1, + [&](auto task_id) { + td::int64 local_cell_count = 0; + td::int64 local_desc_count = 0; + CHECK(!DataCell::use_arena); + DataCell::use_arena = use_arena; + kv->for_each_in_range(keys.at(task_id), keys.at(task_id + 1), [&](td::Slice key, td::Slice value) { + if (td::begins_with(key, "desc") && key.size() != 32) { + local_desc_count++; + return td::Status::OK(); + } + auto r_res = CellLoader::load(key, value.str(), true, pc_creator); + if (r_res.is_error()) { + LOG(ERROR) << r_res.error() << " at " << td::format::escaped(key); + return td::Status::OK(); + } + CHECK(key.size() == 32); + CHECK(key.ubegin()[0] == task_id); + auto res = r_res.move_as_ok(); + f(res.refcnt(), res.cell()); + local_cell_count++; + return td::Status::OK(); + }).ensure(); + DataCell::use_arena = false; + cell_count += local_cell_count; + desc_count += local_desc_count; + }, + options.extra_threads); + return std::make_pair(cell_count.load(), desc_count.load()); + }; + + auto storage = CellStorage::build(options, parallel_scan_cells); + return std::make_unique(std::move(storage)); +} +} // namespace vm diff --git a/crypto/vm/db/StaticBagOfCellsDb.cpp b/crypto/vm/db/StaticBagOfCellsDb.cpp index 9c00a98ce..c667f334b 100644 --- a/crypto/vm/db/StaticBagOfCellsDb.cpp +++ b/crypto/vm/db/StaticBagOfCellsDb.cpp @@ -167,7 +167,7 @@ td::Result> StaticBagOfCellsDb::create_ext_cell(Cell::LevelMask level_ // class StaticBagOfCellsDbBaselineImpl : public StaticBagOfCellsDb { public: - StaticBagOfCellsDbBaselineImpl(std::vector> roots) : roots_(std::move(roots)) { + explicit StaticBagOfCellsDbBaselineImpl(std::vector> roots) : roots_(std::move(roots)) { } td::Result get_root_count() override { return roots_.size(); @@ -233,7 +233,7 @@ class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb { return create_root_cell(std::move(data_cell)); }; - ~StaticBagOfCellsDbLazyImpl() { + ~StaticBagOfCellsDbLazyImpl() override { //LOG(ERROR) << deserialize_cell_cnt_ << " " << deserialize_cell_hash_cnt_; get_thread_safe_counter().add(-1); } @@ -314,11 +314,11 @@ class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb { td::RwMutex::ReadLock guard; if (info_.has_index) { TRY_RESULT(new_offset_view, data_.view(td::MutableSlice(arr, info_.offset_byte_size), - info_.index_offset + idx * info_.offset_byte_size)); + info_.index_offset + (td::int64)idx * info_.offset_byte_size)); offset_view = new_offset_view; } else { guard = index_data_rw_mutex_.lock_read().move_as_ok(); - offset_view = td::Slice(index_data_).substr(idx * info_.offset_byte_size, info_.offset_byte_size); + offset_view = td::Slice(index_data_).substr((td::int64)idx * info_.offset_byte_size, info_.offset_byte_size); } CHECK(offset_view.size() == (size_t)info_.offset_byte_size); @@ -332,7 +332,7 @@ class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb { } char arr[8]; TRY_RESULT(idx_view, data_.view(td::MutableSlice(arr, info_.ref_byte_size), - info_.roots_offset + root_i * info_.ref_byte_size)); + info_.roots_offset + (td::int64)root_i * info_.ref_byte_size)); CHECK(idx_view.size() == (size_t)info_.ref_byte_size); return info_.read_ref(idx_view.ubegin()); } diff --git a/crypto/vm/db/TonDb.h b/crypto/vm/db/TonDb.h index dfce31104..6cd8aa4fa 100644 --- a/crypto/vm/db/TonDb.h +++ b/crypto/vm/db/TonDb.h @@ -113,7 +113,8 @@ class TonDbTransactionImpl; using TonDbTransaction = std::unique_ptr; class TonDbTransactionImpl { public: - SmartContractDb begin_smartcontract(td::Slice hash = {}); + + SmartContractDb begin_smartcontract(td::Slice hash = std::string(32, '\0')); void commit_smartcontract(SmartContractDb txn); void commit_smartcontract(SmartContractDiff txn); @@ -142,6 +143,20 @@ class TonDbTransactionImpl { friend bool operator<(td::Slice hash, const SmartContractInfo &info) { return hash < info.hash; } + + struct Eq { + using is_transparent = void; // Pred to use + bool operator()(const SmartContractInfo &info, const SmartContractInfo &other_info) const { return info.hash == other_info.hash;} + bool operator()(const SmartContractInfo &info, td::Slice hash) const { return info.hash == hash;} + bool operator()(td::Slice hash, const SmartContractInfo &info) const { return info.hash == hash;} + + }; + struct Hash { + using is_transparent = void; // Pred to use + using transparent_key_equal = Eq; + size_t operator()(td::Slice hash) const { return cell_hash_slice_hash(hash); } + size_t operator()(const SmartContractInfo &info) const { return cell_hash_slice_hash(info.hash);} + }; }; CellHashTable contracts_; diff --git a/crypto/vm/dict.cpp b/crypto/vm/dict.cpp index c79924d0e..41f9c3396 100644 --- a/crypto/vm/dict.cpp +++ b/crypto/vm/dict.cpp @@ -1779,7 +1779,7 @@ Ref DictionaryFixed::dict_combine_with(Ref dict1, Ref dict2, t int mode, int skip1, int skip2) const { if (dict1.is_null()) { assert(!skip2); - if ((mode & 1) && dict2.is_null()) { + if ((mode & 1) && dict2.not_null()) { throw CombineError{}; } return dict2; @@ -1854,11 +1854,11 @@ Ref DictionaryFixed::dict_combine_with(Ref dict1, Ref dict2, t key_buffer[-1] = 0; // combine left subtrees auto c1 = dict_combine_with(label1.remainder->prefetch_ref(0), label2.remainder->prefetch_ref(0), key_buffer, - n - c - 1, total_key_len, combine_func); + n - c - 1, total_key_len, combine_func, mode); key_buffer[-1] = 1; // combine right subtrees auto c2 = dict_combine_with(label1.remainder->prefetch_ref(1), label2.remainder->prefetch_ref(1), key_buffer, - n - c - 1, total_key_len, combine_func); + n - c - 1, total_key_len, combine_func, mode); label1.remainder.clear(); label2.remainder.clear(); // c1 and c2 are merged left and right children of dict1 and dict2 diff --git a/crypto/vm/large-boc-serializer.cpp b/crypto/vm/large-boc-serializer.cpp index a7dae1b08..d209c88ed 100644 --- a/crypto/vm/large-boc-serializer.cpp +++ b/crypto/vm/large-boc-serializer.cpp @@ -33,10 +33,12 @@ class LargeBocSerializer { public: using Hash = Cell::Hash; - explicit LargeBocSerializer(std::shared_ptr reader, td::CancellationToken cancellation_token = {}) - : reader(std::move(reader)), cancellation_token(std::move(cancellation_token)) { + explicit LargeBocSerializer(std::shared_ptr reader) : reader(std::move(reader)) { } + void set_logger(BagOfCellsLogger* logger_ptr) { + logger_ptr_ = logger_ptr; + } void add_root(Hash root); td::Status import_cells(); td::Status serialize(td::FileFd& fd, int mode); @@ -44,6 +46,7 @@ class LargeBocSerializer { private: std::shared_ptr reader; struct CellInfo { + Cell::Hash hash; std::array ref_idx; int idx; unsigned short serialized_size; @@ -67,7 +70,7 @@ class LargeBocSerializer { return 4; } }; - std::map cells; + td::NodeHashMap cells; std::vector*> cell_list; struct RootInfo { RootInfo(Hash hash, int idx) : hash(hash), idx(idx) { @@ -85,10 +88,7 @@ class LargeBocSerializer { int revisit(int cell_idx, int force = 0); td::uint64 compute_sizes(int mode, int& r_size, int& o_size); - td::CancellationToken cancellation_token; - td::Timestamp log_speed_at_; - size_t processed_cells_ = 0; - static constexpr double LOG_SPEED_PERIOD = 120.0; + BagOfCellsLogger* logger_ptr_{}; }; void LargeBocSerializer::add_root(Hash root) { @@ -96,16 +96,18 @@ void LargeBocSerializer::add_root(Hash root) { } td::Status LargeBocSerializer::import_cells() { - td::Timer timer; - log_speed_at_ = td::Timestamp::in(LOG_SPEED_PERIOD); - processed_cells_ = 0; + if (logger_ptr_) { + logger_ptr_->start_stage("import_cells"); + } for (auto& root : roots) { TRY_RESULT(idx, import_cell(root.hash)); root.idx = idx; } reorder_cells(); CHECK(!cell_list.empty()); - LOG(ERROR) << "serializer: import_cells took " << timer.elapsed() << "s, " << cell_count << " cells"; + if (logger_ptr_) { + logger_ptr_->finish_stage(PSLICE() << cell_count << " cells"); + } return td::Status::OK(); } @@ -113,14 +115,8 @@ td::Result LargeBocSerializer::import_cell(Hash hash, int depth) { if (depth > Cell::max_depth) { return td::Status::Error("error while importing a cell into a bag of cells: cell depth too large"); } - ++processed_cells_; - if (processed_cells_ % 1000 == 0) { - TRY_STATUS(cancellation_token.check()); - } - if (log_speed_at_.is_in_past()) { - log_speed_at_ += LOG_SPEED_PERIOD; - LOG(WARNING) << "serializer: import_cells " << (double)processed_cells_ / LOG_SPEED_PERIOD << " cells/s"; - processed_cells_ = 0; + if (logger_ptr_) { + TRY_STATUS(logger_ptr_->on_cell_processed()); } auto it = cells.find(hash); if (it != cells.end()) { @@ -306,7 +302,6 @@ td::uint64 LargeBocSerializer::compute_sizes(int mode, int& r_size, int& o_size) } td::Status LargeBocSerializer::serialize(td::FileFd& fd, int mode) { - td::Timer timer; using Mode = BagOfCells::Mode; BagOfCells::Info info; if ((mode & Mode::WithCacheBits) && !(mode & Mode::WithIndex)) { @@ -370,6 +365,9 @@ td::Status LargeBocSerializer::serialize(td::FileFd& fd, int mode) { DCHECK(writer.position() == info.index_offset); DCHECK((unsigned)cell_count == cell_list.size()); if (info.has_index) { + if (logger_ptr_) { + logger_ptr_->start_stage("generate_index"); + } std::size_t offs = 0; for (int i = cell_count - 1; i >= 0; --i) { const auto& dc_info = cell_list[i]->second; @@ -387,13 +385,20 @@ td::Status LargeBocSerializer::serialize(td::FileFd& fd, int mode) { fixed_offset = offs * 2 + dc_info.should_cache; } store_offset(fixed_offset); + if (logger_ptr_) { + TRY_STATUS(logger_ptr_->on_cell_processed()); + } } DCHECK(offs == info.data_size); + if (logger_ptr_) { + logger_ptr_->finish_stage(""); + } } DCHECK(writer.position() == info.data_offset); size_t keep_position = writer.position(); - log_speed_at_ = td::Timestamp::in(LOG_SPEED_PERIOD); - processed_cells_ = 0; + if (logger_ptr_) { + logger_ptr_->start_stage("serialize"); + } for (int i = 0; i < cell_count; ++i) { auto hash = cell_list[cell_count - 1 - i]->first; const auto& dc_info = cell_list[cell_count - 1 - i]->second; @@ -412,14 +417,8 @@ td::Status LargeBocSerializer::serialize(td::FileFd& fd, int mode) { DCHECK(k > i && k < cell_count); store_ref(k); } - ++processed_cells_; - if (processed_cells_ % 1000 == 0) { - TRY_STATUS(cancellation_token.check()); - } - if (log_speed_at_.is_in_past()) { - log_speed_at_ += LOG_SPEED_PERIOD; - LOG(WARNING) << "serializer: serialize " << (double)processed_cells_ / LOG_SPEED_PERIOD << " cells/s"; - processed_cells_ = 0; + if (logger_ptr_) { + TRY_STATUS(logger_ptr_->on_cell_processed()); } } DCHECK(writer.position() - keep_position == info.data_size); @@ -429,8 +428,9 @@ td::Status LargeBocSerializer::serialize(td::FileFd& fd, int mode) { } DCHECK(writer.empty()); TRY_STATUS(writer.finalize()); - LOG(ERROR) << "serializer: serialize took " << timer.elapsed() << "s, " << cell_count << " cells, " - << writer.position() << " bytes"; + if (logger_ptr_) { + logger_ptr_->finish_stage(PSLICE() << cell_count << " cells, " << writer.position() << " bytes"); + } return td::Status::OK(); } } // namespace @@ -439,7 +439,9 @@ td::Status std_boc_serialize_to_file_large(std::shared_ptr reader, int mode, td::CancellationToken cancellation_token) { td::Timer timer; CHECK(reader != nullptr) - LargeBocSerializer serializer(reader, std::move(cancellation_token)); + LargeBocSerializer serializer(reader); + BagOfCellsLogger logger(std::move(cancellation_token)); + serializer.set_logger(&logger); serializer.add_root(root_hash); TRY_STATUS(serializer.import_cells()); TRY_STATUS(serializer.serialize(fd, mode)); diff --git a/crypto/vm/vm.h b/crypto/vm/vm.h index e5cca026c..7afa355c3 100644 --- a/crypto/vm/vm.h +++ b/crypto/vm/vm.h @@ -118,6 +118,7 @@ class VmState final : public VmStateInterface { stack_entry_gas_price = 1, runvm_gas_price = 40, hash_ext_entry_gas_price = 1, + free_nested_cont_jump = 8, rist255_mul_gas_price = 2000, rist255_mulbase_gas_price = 750, @@ -366,11 +367,19 @@ class VmState final : public VmStateInterface { return cond ? c1_envelope(std::move(cont), save) : std::move(cont); } void c1_save_set(bool save = true); - void fatal(void) const { + void fatal() const { throw VmFatal{}; } int jump_to(Ref cont) { - return cont->is_unique() ? cont.unique_write().jump_w(this) : cont->jump(this); + int res = 0, cnt = 0; + while (cont.not_null()) { + cont = cont->is_unique() ? cont.unique_write().jump_w(this, res) : cont->jump(this, res); + cnt++; + if (cnt > free_nested_cont_jump && global_version >= 9) { + consume_gas(1); + } + } + return res; } static Ref convert_code_cell(Ref code_cell); bool try_commit(); diff --git a/doc/GlobalVersions.md b/doc/GlobalVersions.md index e649c009a..36d1ab360 100644 --- a/doc/GlobalVersions.md +++ b/doc/GlobalVersions.md @@ -109,4 +109,10 @@ Operations for working with Merkle proofs, where cells can have non-zero level a - Slightly change random seed generation to fix mix of `addr_rewrite` and `addr`. - Fill in `skipped_actions` for both invalid and valid messages with `IGNORE_ERROR` mode that can't be sent. - Allow unfreeze through external messages. -- Don't use user-provided `fwd_fee` and `ihr_fee` for internal messages. \ No newline at end of file +- Don't use user-provided `fwd_fee` and `ihr_fee` for internal messages. + +## Version 9 + +- Fix `RAWRESERVE` action with flag `4` (use original balance of the account) by explicitly setting `original_balance` to `balance - msg_balance_remaining`. + - Previously it did not work if storage fee was greater than the original balance. +- Jumps to nested continuations of depth more than 8 consume 1 gas for eact subsequent continuation (this does not affect most of TVM code). \ No newline at end of file diff --git a/emulator/CMakeLists.txt b/emulator/CMakeLists.txt index dc8cbf62b..4b6383005 100644 --- a/emulator/CMakeLists.txt +++ b/emulator/CMakeLists.txt @@ -27,7 +27,7 @@ set(EMULATOR_EMSCRIPTEN_SOURCE include(GenerateExportHeader) add_library(emulator_static STATIC ${EMULATOR_STATIC_SOURCE}) -target_link_libraries(emulator_static PUBLIC ton_crypto ton_block smc-envelope) +target_link_libraries(emulator_static PUBLIC ton_crypto smc-envelope) if (NOT USE_EMSCRIPTEN AND BUILD_SHARED_LIBS) add_library(emulator SHARED ${EMULATOR_SOURCE} ${EMULATOR_HEADERS}) @@ -65,4 +65,4 @@ if (USE_EMSCRIPTEN) target_compile_options(emulator-emscripten PRIVATE -fexceptions) endif() -install(TARGETS emulator LIBRARY DESTINATION lib) +install(TARGETS emulator ARCHIVE DESTINATION lib LIBRARY DESTINATION lib) diff --git a/keys/keys.hpp b/keys/keys.hpp index 72d0845ac..2e517b2e4 100644 --- a/keys/keys.hpp +++ b/keys/keys.hpp @@ -260,6 +260,10 @@ class PublicKey { td::BufferSlice export_as_slice() const; static td::Result import(td::Slice s); + bool is_ed25519() const { + return pub_key_.get_offset() == pub_key_.offset(); + } + pubkeys::Ed25519 ed25519_value() const { CHECK(pub_key_.get_offset() == pub_key_.offset()); return pub_key_.get(); diff --git a/lite-client/CMakeLists.txt b/lite-client/CMakeLists.txt index 598a8d28c..c6988cf56 100644 --- a/lite-client/CMakeLists.txt +++ b/lite-client/CMakeLists.txt @@ -1,10 +1,9 @@ cmake_minimum_required(VERSION 3.5 FATAL_ERROR) add_library(lite-client-common STATIC lite-client-common.cpp lite-client-common.h) -target_link_libraries(lite-client-common PUBLIC tdutils tdactor adnllite tl_api tl_lite_api tl-lite-utils ton_crypto ton_block) +target_link_libraries(lite-client-common PUBLIC tdactor adnllite tl_api tl_lite_api tl-lite-utils ton_crypto) add_executable(lite-client lite-client.cpp lite-client.h) -target_link_libraries(lite-client tdutils tdactor adnllite tl_api tl_lite_api tl-lite-utils ton_crypto ton_block - terminal lite-client-common git) +target_link_libraries(lite-client tdutils tdactor adnllite tl_api tl_lite_api tl-lite-utils terminal lite-client-common git) install(TARGETS lite-client RUNTIME DESTINATION bin) diff --git a/lite-client/lite-client.cpp b/lite-client/lite-client.cpp index 64383730b..1a4201a7c 100644 --- a/lite-client/lite-client.cpp +++ b/lite-client/lite-client.cpp @@ -965,8 +965,8 @@ bool TestNode::show_help(std::string command) { "recentcreatorstats [ []]\tLists block creator statistics " "updated after by validator public " "key\n" - "checkload[all|severe] []\tChecks whether all validators worked " - "properly during specified time " + "checkload[all|severe][-v2] []\tChecks whether all validators " + "worked properly during specified time " "interval, and optionally saves proofs into -.boc\n" "loadproofcheck \tChecks a validator misbehavior proof previously created by checkload\n" "pastvalsets\tLists known past validator set ids and their hashes\n" @@ -974,6 +974,11 @@ bool TestNode::show_help(std::string command) { "into files .boc\n" "complaintprice \tComputes the price (in nanograms) for creating a complaint\n" "msgqueuesizes\tShows current sizes of outbound message queues in all shards\n" + "dispatchqueueinfo \tShows list of account dispatch queue of a block\n" + "dispatchqueuemessages []\tShows deferred messages from account , lt > " + "\n" + "dispatchqueuemessagesall [ []]\tShows messages from dispatch queue of a " + "block, starting after , \n" "known\tShows the list of all known block ids\n" "knowncells\tShows the list of hashes of all known (cached) cells\n" "dumpcell \nDumps a cached cell by a prefix of its hash\n" @@ -988,9 +993,9 @@ bool TestNode::show_help(std::string command) { bool TestNode::do_parse_line() { ton::WorkchainId workchain = ton::masterchainId; // change to basechain later int addr_ext = 0; - ton::StdSmcAddress addr{}; + ton::StdSmcAddress addr = ton::StdSmcAddress::zero(); ton::BlockIdExt blkid{}; - ton::LogicalTime lt{}; + ton::LogicalTime lt = 0; ton::Bits256 hash{}; ton::ShardIdFull shard{}; ton::BlockSeqno seqno{}; @@ -1097,8 +1102,15 @@ bool TestNode::do_parse_line() { return parse_block_id_ext(blkid) && (!mode || parse_uint32(utime)) && (seekeoln() ? (mode |= 0x100) : parse_uint32(count)) && (seekeoln() || (parse_hash(hash) && (mode |= 1))) && seekeoln() && get_creator_stats(blkid, mode, count, hash, utime); - } else if (word == "checkload" || word == "checkloadall" || word == "checkloadsevere") { - int time1, time2, mode = (word == "checkloadsevere"); + } else if (word == "checkload" || word == "checkloadall" || word == "checkloadsevere" || word == "checkload-v2" || + word == "checkloadall-v2" || word == "checkloadsevere-v2") { + int time1, time2, mode = 0; + if (word == "checkloadsevere" || word == "checkloadsevere-v2") { + mode |= 1; + } + if (td::ends_with(word, "-v2")) { + mode |= 4; + } std::string file_pfx; return parse_int32(time1) && parse_int32(time2) && (seekeoln() || ((mode |= 2) && get_word_to(file_pfx))) && seekeoln() && check_validator_load(time1, time2, mode, file_pfx); @@ -1118,6 +1130,16 @@ bool TestNode::do_parse_line() { set_error(get_complaint_price(expire_in, filename)); } else if (word == "msgqueuesizes") { return get_msg_queue_sizes(); + } else if (word == "dispatchqueueinfo") { + return parse_block_id_ext(blkid) && seekeoln() && get_dispatch_queue_info(blkid); + } else if (word == "dispatchqueuemessages" || word == "dispatchqueuemessagesall") { + bool one_account = word == "dispatchqueuemessages"; + if (!parse_block_id_ext(blkid)) { + return false; + } + workchain = blkid.id.workchain; + return ((!one_account && seekeoln()) || parse_account_addr(workchain, addr)) && (seekeoln() || parse_lt(lt)) && + seekeoln() && get_dispatch_queue_messages(blkid, workchain, addr, lt, one_account); } else if (word == "known") { return eoln() && show_new_blkids(true); } else if (word == "knowncells") { @@ -1645,6 +1667,81 @@ void TestNode::got_msg_queue_sizes(ton::tl_object_ptrext_msg_queue_size_limit_ << std::endl; } +bool TestNode::get_dispatch_queue_info(ton::BlockIdExt block_id) { + td::TerminalIO::out() << "Dispatch queue in block: " << block_id.id.to_str() << std::endl; + return get_dispatch_queue_info_cont(block_id, true, td::Bits256::zero()); +} + +bool TestNode::get_dispatch_queue_info_cont(ton::BlockIdExt block_id, bool first, td::Bits256 after_addr) { + auto q = ton::create_serialize_tl_object( + first ? 0 : 2, ton::create_tl_lite_block_id(block_id), after_addr, 32, false); + return envelope_send_query(std::move(q), [=, Self = actor_id(this)](td::Result res) -> void { + if (res.is_error()) { + LOG(ERROR) << "liteServer.getDispatchQueueInfo error: " << res.move_as_error(); + return; + } + auto F = ton::fetch_tl_object(res.move_as_ok(), true); + if (F.is_error()) { + LOG(ERROR) << "cannot parse answer to liteServer.getDispatchQueueInfo"; + return; + } + td::actor::send_closure_later(Self, &TestNode::got_dispatch_queue_info, block_id, F.move_as_ok()); + }); +} + +void TestNode::got_dispatch_queue_info(ton::BlockIdExt block_id, + ton::tl_object_ptr info) { + for (auto& acc : info->account_dispatch_queues_) { + td::TerminalIO::out() << block_id.id.workchain << ":" << acc->addr_.to_hex() << " : size=" << acc->size_ + << " lt=" << acc->min_lt_ << ".." << acc->max_lt_ << std::endl; + } + if (info->complete_) { + td::TerminalIO::out() << "Done" << std::endl; + return; + } + get_dispatch_queue_info_cont(block_id, false, info->account_dispatch_queues_.back()->addr_); +} + +bool TestNode::get_dispatch_queue_messages(ton::BlockIdExt block_id, ton::WorkchainId wc, ton::StdSmcAddress addr, + ton::LogicalTime lt, bool one_account) { + if (wc != block_id.id.workchain) { + return set_error("workchain mismatch"); + } + auto q = ton::create_serialize_tl_object( + one_account ? 2 : 0, ton::create_tl_lite_block_id(block_id), addr, lt, 64, false, one_account, false); + return envelope_send_query(std::move(q), [=, Self = actor_id(this)](td::Result res) -> void { + if (res.is_error()) { + LOG(ERROR) << "liteServer.getDispatchQueueMessages error: " << res.move_as_error(); + return; + } + auto F = ton::fetch_tl_object(res.move_as_ok(), true); + if (F.is_error()) { + LOG(ERROR) << "cannot parse answer to liteServer.getDispatchQueueMessages"; + return; + } + td::actor::send_closure_later(Self, &TestNode::got_dispatch_queue_messages, F.move_as_ok()); + }); +} + +void TestNode::got_dispatch_queue_messages(ton::tl_object_ptr msgs) { + td::TerminalIO::out() << "Dispatch queue messages (" << msgs->messages_.size() << "):\n"; + int count = 0; + for (auto& m : msgs->messages_) { + auto& meta = m->metadata_; + td::TerminalIO::out() << "Msg #" << ++count << ": " << msgs->id_->workchain_ << ":" << m->addr_.to_hex() << " " + << m->lt_ << " : " + << (meta->initiator_->workchain_ == ton::workchainInvalid + ? "[ no metadata ]" + : block::MsgMetadata{(td::uint32)meta->depth_, meta->initiator_->workchain_, + meta->initiator_->id_, (ton::LogicalTime)meta->initiator_lt_} + .to_str()) + << "\n"; + } + if (!msgs->complete_) { + td::TerminalIO::out() << "(incomplete list)\n"; + } +} + bool TestNode::dns_resolve_start(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt blkid, std::string domain, td::Bits256 cat, int mode) { if (domain.size() >= 2 && domain[0] == '"' && domain.back() == '"') { @@ -3616,7 +3713,7 @@ void TestNode::continue_check_validator_load2(std::unique_ptr info2, int mode, std::string file_pfx) { LOG(INFO) << "continue_check_validator_load3 for blocks " << info1->blk_id.to_str() << " and " - << info2->blk_id.to_str() << " with mode=" << mode << " and file prefix `" << file_pfx - << "`: comparing block creators data"; + << info2->blk_id.to_str() << " with mode=" << mode << " and file prefix `" << file_pfx; + + if (mode & 4) { + ton::BlockSeqno start_seqno = info1->blk_id.seqno(); + ton::BlockSeqno end_seqno = info2->blk_id.seqno(); + block::ValidatorSet validator_set = *info1->vset; + if (info1->config->get_config_param(28)->get_hash() != info2->config->get_config_param(28)->get_hash()) { + LOG(ERROR) << "Catchain validator config (28) changed between the first and the last block"; + return; + } + auto catchain_config = std::make_unique( + block::Config::unpack_catchain_validators_config(info1->config->get_config_param(28))); + load_validator_shard_shares( + start_seqno, end_seqno, std::move(validator_set), std::move(catchain_config), + [=, this, info1 = std::move(info1), + info2 = std::move(info2)](td::Result> R) mutable { + if (R.is_error()) { + LOG(ERROR) << "failed to load validator shard shares: " << R.move_as_error(); + } else { + continue_check_validator_load4(std::move(info1), std::move(info2), mode, file_pfx, R.move_as_ok()); + } + }); + } else { + continue_check_validator_load4(std::move(info1), std::move(info2), mode, std::move(file_pfx), {}); + } +} + +void TestNode::continue_check_validator_load4(std::unique_ptr info1, + std::unique_ptr info2, int mode, + std::string file_pfx, + std::map exact_shard_shares) { + LOG(INFO) << "continue_check_validator_load4 for blocks " << info1->blk_id.to_str() << " and " + << info2->blk_id.to_str() << " with mode=" << mode << " and file prefix `" << file_pfx; if (info1->created_total.first <= 0 || info2->created_total.first <= 0) { LOG(ERROR) << "no total created blocks statistics"; return; } td::TerminalIO::out() << "total: (" << info1->created_total.first << "," << info1->created_total.second << ") -> (" << info2->created_total.first << "," << info2->created_total.second << ")\n"; - auto x = info2->created_total.first - info1->created_total.first; - auto y = info2->created_total.second - info1->created_total.second; - td::int64 xs = 0, ys = 0; - if (x <= 0 || y < 0 || (x | y) >= (1u << 31)) { - LOG(ERROR) << "impossible situation: zero or no blocks created: " << x << " masterchain blocks, " << y - << " shardchain blocks"; + auto created_total_mc = info2->created_total.first - info1->created_total.first; + auto created_total_bc = info2->created_total.second - info1->created_total.second; + td::int64 created_mc_sum = 0, created_bc_sum = 0; + if (created_total_mc <= 0 || created_total_bc < 0 || (created_total_mc | created_total_bc) >= (1U << 31)) { + LOG(ERROR) << "impossible situation: zero or no blocks created: " << created_total_mc << " masterchain blocks, " + << created_total_bc << " shardchain blocks"; return; } - std::pair created_total{(int)x, (int)y}; int count = info1->vset->total; CHECK(info2->vset->total == count); CHECK((int)info1->created.size() == count); CHECK((int)info2->created.size() == count); - std::vector> d; - d.reserve(count); + std::vector> vals_created; + vals_created.reserve(count); for (int i = 0; i < count; i++) { - auto x1 = info2->created[i].first - info1->created[i].first; - auto y1 = info2->created[i].second - info1->created[i].second; - if (x1 < 0 || y1 < 0 || (x1 | y1) >= (1u << 31)) { - LOG(ERROR) << "impossible situation: validator #" << i << " created a negative amount of blocks: " << x1 - << " masterchain blocks, " << y1 << " shardchain blocks"; + auto created_mc = info2->created[i].first - info1->created[i].first; + auto created_bc = info2->created[i].second - info1->created[i].second; + if (created_mc < 0 || created_bc < 0 || (created_mc | created_bc) >= (1u << 31)) { + LOG(ERROR) << "impossible situation: validator #" << i << " created a negative amount of blocks: " << created_mc + << " masterchain blocks, " << created_bc << " shardchain blocks"; return; } - xs += x1; - ys += y1; - d.emplace_back((int)x1, (int)y1); - td::TerminalIO::out() << "val #" << i << ": created (" << x1 << "," << y1 << ") ; was (" << info1->created[i].first - << "," << info1->created[i].second << ")\n"; + created_mc_sum += created_mc; + created_bc_sum += created_bc; + vals_created.emplace_back((int)created_mc, (int)created_bc); + td::TerminalIO::out() << "val #" << i << ": created (" << created_mc << "," << created_bc << ") ; was (" + << info1->created[i].first << "," << info1->created[i].second << ")\n"; } - if (xs != x || ys != y) { - LOG(ERROR) << "cannot account for all blocks created: total is (" << x << "," << y - << "), but the sum for all validators is (" << xs << "," << ys << ")"; + if (created_mc_sum != created_total_mc || created_bc_sum != created_total_bc) { + LOG(ERROR) << "cannot account for all blocks created: total is (" << created_total_mc << "," << created_total_bc + << "), but the sum for all validators is (" << created_mc_sum << "," << created_bc_sum << ")"; return; } - td::TerminalIO::out() << "total: (" << x << "," << y << ")\n"; + td::TerminalIO::out() << "total: (" << created_total_mc << "," << created_total_bc << ")\n"; auto ccfg = block::Config::unpack_catchain_validators_config(info2->config->get_config_param(28)); auto ccfg_old = block::Config::unpack_catchain_validators_config(info1->config->get_config_param(28)); if (ccfg.shard_val_num != ccfg_old.shard_val_num || ccfg.shard_val_num <= 0) { @@ -3707,55 +3834,214 @@ void TestNode::continue_check_validator_load3(std::unique_ptrvset->main; - if (info1->vset->main != main_count || main_count <= 0) { - LOG(ERROR) << "masterchain validator group size changed from " << info1->vset->main << " to " << main_count + int shard_vals = ccfg.shard_val_num, master_vals = info2->vset->main; + if (info1->vset->main != master_vals || master_vals <= 0) { + LOG(ERROR) << "masterchain validator group size changed from " << info1->vset->main << " to " << master_vals << ", or is not positive"; return; } - int cnt = 0, cnt_ok = 0; - double chunk_size = ccfg.shard_val_lifetime / 3. / shard_count; - block::MtCarloComputeShare shard_share(shard_count, info2->vset->export_scaled_validator_weights()); + + bool use_exact_shard_share = mode & 4; + int proofs_cnt = 0, proofs_cnt_ok = 0; + double chunk_size = ccfg.shard_val_lifetime / 3. / shard_vals; + + std::vector mtc_shard_share; + if (use_exact_shard_share) { + LOG(INFO) << "using exact shard shares"; + td::uint64 exact_shard_shares_sum = 0; + for (auto& [_, count] : exact_shard_shares) { + exact_shard_shares_sum += count; + } + if ((td::int64)exact_shard_shares_sum != shard_vals * created_bc_sum) { + LOG(ERROR) << "unexpected total shard shares: blocks=" << created_bc_sum << ", shard_vals=" << shard_vals + << ", expected_sum=" << shard_vals * created_bc_sum << ", found=" << exact_shard_shares_sum; + return; + } + } else { + LOG(INFO) << "using MtCarloComputeShare"; + block::MtCarloComputeShare mtc(shard_vals, info2->vset->export_scaled_validator_weights()); + if (!mtc.is_ok()) { + LOG(ERROR) << "failed to compute shard shares"; + return; + } + mtc_shard_share.resize(count); + for (size_t i = 0; i < count; ++i) { + mtc_shard_share[i] = mtc[i]; + } + } + + auto validators = info1->vset->export_validator_set(); for (int i = 0; i < count; i++) { - int x1 = d[i].first, y1 = d[i].second; - bool is_masterchain_validator = i < main_count; - double xe = (is_masterchain_validator ? (double)xs / main_count : 0); - double ye = shard_share[i] * (double)ys / shard_count; + int created_mc = vals_created[i].first, created_bc = vals_created[i].second; + bool is_masterchain_validator = i < master_vals; + + double expected_created_mc = (is_masterchain_validator ? (double)created_mc_sum / master_vals : 0); + double prob_mc = create_prob(created_mc, .9 * expected_created_mc); + + double expected_created_bc, prob_bc; + if (use_exact_shard_share) { + expected_created_bc = (double)exact_shard_shares[validators[i].key.as_bits256()] / shard_vals; + prob_bc = create_prob(created_bc, .9 * expected_created_bc); + } else { + expected_created_bc = mtc_shard_share[i] * (double)created_bc_sum / shard_vals; + prob_bc = shard_create_prob(created_bc, .9 * expected_created_bc, chunk_size); + } + td::Bits256 pk = info2->vset->list[i].pubkey.as_bits256(); - double p1 = create_prob(x1, .9 * xe), p2 = shard_create_prob(y1, .9 * ye, chunk_size); - td::TerminalIO::out() << "val #" << i << ": pubkey " << pk.to_hex() << ", blocks created (" << x1 << "," << y1 - << "), expected (" << xe << "," << ye << "), probabilities " << p1 << " and " << p2 << "\n"; - if ((is_masterchain_validator ? p1 : p2) < .00001) { + td::TerminalIO::out() << "val #" << i << ": pubkey " << pk.to_hex() << ", blocks created (" << created_mc << "," + << created_bc << "), expected (" << expected_created_mc << "," << expected_created_bc + << "), probabilities " << prob_mc << " and " << prob_bc << "\n"; + if ((is_masterchain_validator ? prob_mc : prob_bc) < .00001) { LOG(ERROR) << "validator #" << i << " with pubkey " << pk.to_hex() << " : serious misbehavior detected: created less than 90% of the expected amount of blocks with " "probability 99.999% : created (" - << x1 << "," << y1 << "), expected (" << xe << "," << ye << ") masterchain/shardchain blocks\n"; + << created_mc << "," << created_bc << "), expected (" << expected_created_mc << "," + << expected_created_bc << ") masterchain/shardchain blocks\n"; if (mode & 2) { - auto st = write_val_create_proof(*info1, *info2, i, true, file_pfx, ++cnt); + auto st = write_val_create_proof(*info1, *info2, i, true, file_pfx, ++proofs_cnt); if (st.is_error()) { LOG(ERROR) << "cannot create proof: " << st.move_as_error(); } else { - cnt_ok++; + proofs_cnt_ok++; } } - } else if ((is_masterchain_validator ? p1 : p2) < .005) { + } else if ((is_masterchain_validator ? prob_mc : prob_bc) < .005) { LOG(ERROR) << "validator #" << i << " with pubkey " << pk.to_hex() << " : moderate misbehavior detected: created less than 90% of the expected amount of blocks with " "probability 99.5% : created (" - << x1 << "," << y1 << "), expected (" << xe << "," << ye << ") masterchain/shardchain blocks\n"; + << created_mc << "," << created_bc << "), expected (" << expected_created_mc << "," + << expected_created_bc << ") masterchain/shardchain blocks\n"; if ((mode & 3) == 2) { - auto st = write_val_create_proof(*info1, *info2, i, false, file_pfx, ++cnt); + auto st = write_val_create_proof(*info1, *info2, i, false, file_pfx, ++proofs_cnt); if (st.is_error()) { LOG(ERROR) << "cannot create proof: " << st.move_as_error(); } else { - cnt_ok++; + proofs_cnt_ok++; } } } } - if (cnt > 0) { - LOG(INFO) << cnt_ok << " out of " << cnt << " proofs written to " << file_pfx << "-*.boc"; + if (proofs_cnt > 0) { + LOG(INFO) << proofs_cnt_ok << " out of " << proofs_cnt << " proofs written to " << file_pfx << "-*.boc"; + } +} + +void TestNode::load_validator_shard_shares(ton::BlockSeqno start_seqno, ton::BlockSeqno end_seqno, + block::ValidatorSet validator_set, + std::unique_ptr catchain_config, + td::Promise> promise) { + CHECK(start_seqno <= end_seqno); + LOG(INFO) << "loading shard shares from mc blocks " << start_seqno << ".." << end_seqno << " (" + << end_seqno - start_seqno + 1 << " blocks)"; + auto state = std::make_shared(); + state->start_seqno = start_seqno; + state->end_seqno = end_seqno; + state->validator_set = std::move(validator_set); + state->catchain_config = std::move(catchain_config); + state->shard_configs.resize(end_seqno - start_seqno + 1); + state->promise = std::move(promise); + load_validator_shard_shares_cont(std::move(state)); +} + +void TestNode::load_validator_shard_shares_cont(std::shared_ptr state) { + if (!state->promise) { + return; + } + if (state->loaded % 100 == 0) { + LOG(INFO) << "loaded " << state->loaded << "/" << state->shard_configs.size() << " mc blocks"; + } + while (state->cur_idx < state->shard_configs.size() && state->pending < 8) { + load_block_shard_configuration(state->start_seqno + state->cur_idx, + [this, state, idx = state->cur_idx](td::Result R) mutable { + if (R.is_error()) { + state->promise.set_error(R.move_as_error()); + state->promise = {}; + } else { + state->shard_configs[idx] = R.move_as_ok(); + --state->pending; + ++state->loaded; + load_validator_shard_shares_cont(std::move(state)); + } + }); + ++state->pending; + ++state->cur_idx; + } + + if (state->loaded != state->shard_configs.size()) { + return; + } + LOG(INFO) << "loaded all " << state->shard_configs.size() << " mc blocks, computing shard shares"; + std::map result; + try { + for (size_t idx = 0; idx + 1 < state->shard_configs.size(); ++idx) { + block::ShardConfig& shards1 = state->shard_configs[idx]; + block::ShardConfig& shards2 = state->shard_configs[idx + 1]; + + // Compute validator groups, see ValidatorManagerImpl::update_shards + auto process_shard = [&](ton::ShardIdFull shard, ton::BlockSeqno first_seqno) { + auto desc2 = shards2.get_shard_hash(shard); + if (desc2.is_null() || desc2->seqno() < first_seqno) { + return; + } + td::uint32 blocks_count = desc2->seqno() - first_seqno + 1; + ton::CatchainSeqno cc_seqno = shards1.get_shard_cc_seqno(shard); + auto val_set = + block::ConfigInfo::do_compute_validator_set(*state->catchain_config, shard, state->validator_set, cc_seqno); + for (const auto &val : val_set) { + result[val.key.as_bits256()] += blocks_count; + } + }; + + for (const ton::BlockId& id : shards1.get_shard_hash_ids()) { + ton::ShardIdFull shard = id.shard_full(); + auto desc = shards1.get_shard_hash(shard); + CHECK(desc.not_null()); + if (desc->before_split()) { + ton::ShardIdFull l_shard = shard_child(shard, true); + ton::ShardIdFull r_shard = shard_child(shard, false); + process_shard(l_shard, desc->seqno() + 1); + process_shard(r_shard, desc->seqno() + 1); + } else if (desc->before_merge()) { + if (is_right_child(shard)) { + continue; + } + ton::ShardIdFull sibling_shard = shard_sibling(shard); + auto sibling_desc = shards1.get_shard_hash(sibling_shard); + CHECK(sibling_desc.not_null()); + ton::ShardIdFull p_shard = shard_parent(shard); + process_shard(p_shard, std::max(desc->seqno(), sibling_desc->seqno()) + 1); + } else { + process_shard(shard, desc->seqno() + 1); + } + } + } + } catch (vm::VmError &e) { + state->promise.set_error(e.as_status("cannot parse shard hashes: ")); + return; } + state->promise.set_value(std::move(result)); +} + +void TestNode::load_block_shard_configuration(ton::BlockSeqno seqno, td::Promise promise) { + lookup_block( + ton::ShardIdFull{ton::masterchainId}, 1, seqno, + [this, promise = std::move(promise)](td::Result R) mutable { + TRY_RESULT_PROMISE(promise, res, std::move(R)); + auto b = ton::serialize_tl_object( + ton::create_tl_object(ton::create_tl_lite_block_id(res.blk_id)), + true); + envelope_send_query(std::move(b), [this, promise = std::move(promise)](td::Result R) mutable { + TRY_RESULT_PROMISE(promise, data, std::move(R)); + TRY_RESULT_PROMISE(promise, f, ton::fetch_tl_object(data, true)); + TRY_RESULT_PROMISE(promise, root, vm::std_boc_deserialize(f->data_)); + block::ShardConfig sh_conf; + if (!sh_conf.unpack(load_cell_slice_ref(root))) { + promise.set_error(td::Status::Error("cannot extract shard block list from shard configuration")); + } else { + promise.set_value(std::move(sh_conf)); + } + }); + }); } bool compute_punishment_default(int interval, bool severe, td::RefInt256& fine, unsigned& fine_part) { @@ -4323,7 +4609,7 @@ int main(int argc, char* argv[]) { }); p.add_option('V', "version", "shows lite-client build information", [&]() { std::cout << "lite-client build information: [ Commit: " << GitMetadata::CommitSHA1() << ", Date: " << GitMetadata::CommitDate() << "]\n"; - + std::exit(0); }); p.add_option('i', "idx", "set liteserver idx", [&](td::Slice arg) { diff --git a/lite-client/lite-client.h b/lite-client/lite-client.h index 17680f448..90a2fb8a4 100644 --- a/lite-client/lite-client.h +++ b/lite-client/lite-client.h @@ -282,6 +282,26 @@ class TestNode : public td::actor::Actor { void continue_check_validator_load3(std::unique_ptr info1, std::unique_ptr info2, int mode = 0, std::string file_pfx = ""); + void continue_check_validator_load4(std::unique_ptr info1, + std::unique_ptr info2, int mode, std::string file_pfx, + std::map exact_shard_shares); + + struct LoadValidatorShardSharesState { + ton::BlockSeqno start_seqno; + ton::BlockSeqno end_seqno; + block::ValidatorSet validator_set; + std::unique_ptr catchain_config; + std::vector shard_configs; + td::uint32 cur_idx = 0, pending = 0, loaded = 0; + td::Promise> promise; + }; + void load_validator_shard_shares(ton::BlockSeqno start_seqno, ton::BlockSeqno end_seqno, + block::ValidatorSet validator_set, + std::unique_ptr catchain_config, + td::Promise> promise); + void load_validator_shard_shares_cont(std::shared_ptr state); + void load_block_shard_configuration(ton::BlockSeqno seqno, td::Promise promise); + 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 load_to, @@ -307,6 +327,13 @@ class TestNode : public td::actor::Actor { unsigned refs, td::Bits256 chash, std::string filename); bool get_msg_queue_sizes(); void got_msg_queue_sizes(ton::tl_object_ptr f); + bool get_dispatch_queue_info(ton::BlockIdExt block_id); + bool get_dispatch_queue_info_cont(ton::BlockIdExt block_id, bool first, td::Bits256 after_addr); + void got_dispatch_queue_info(ton::BlockIdExt block_id, + ton::tl_object_ptr info); + bool get_dispatch_queue_messages(ton::BlockIdExt block_id, ton::WorkchainId wc, ton::StdSmcAddress addr, + ton::LogicalTime lt, bool one_account); + void got_dispatch_queue_messages(ton::tl_object_ptr msgs); bool cache_cell(Ref cell); bool list_cached_cells() const; bool dump_cached_cell(td::Slice hash_pfx, td::Slice type_name = {}); diff --git a/overlay/overlay-broadcast.cpp b/overlay/overlay-broadcast.cpp index 03991b76b..615b3e7c8 100644 --- a/overlay/overlay-broadcast.cpp +++ b/overlay/overlay-broadcast.cpp @@ -68,7 +68,7 @@ td::Status BroadcastSimple::run_checks() { td::Status BroadcastSimple::distribute() { auto B = serialize(); - auto nodes = overlay_->get_neighbours(3); + auto nodes = overlay_->get_neighbours(overlay_->propagate_broadcast_to()); auto manager = overlay_->overlay_manager(); for (auto &n : nodes) { @@ -115,7 +115,8 @@ td::Status BroadcastSimple::run() { return run_continue(); } -td::Status BroadcastSimple::create(OverlayImpl *overlay, adnl::AdnlNodeIdShort src_peer_id, tl_object_ptr broadcast) { +td::Status BroadcastSimple::create(OverlayImpl *overlay, adnl::AdnlNodeIdShort src_peer_id, + tl_object_ptr broadcast) { auto src = PublicKey{broadcast->src_}; auto data_hash = sha256_bits256(broadcast->data_.as_slice()); auto broadcast_hash = compute_broadcast_id(src, data_hash, broadcast->flags_); diff --git a/overlay/overlay-fec-broadcast.cpp b/overlay/overlay-fec-broadcast.cpp index cd030742a..5a0ad10dd 100644 --- a/overlay/overlay-fec-broadcast.cpp +++ b/overlay/overlay-fec-broadcast.cpp @@ -112,7 +112,7 @@ td::Status BroadcastFec::distribute_part(td::uint32 seqno) { td::BufferSlice data_short = std::move(tls.first); td::BufferSlice data = std::move(tls.second); - auto nodes = overlay_->get_neighbours(5); + auto nodes = overlay_->get_neighbours(overlay_->propagate_broadcast_to()); auto manager = overlay_->overlay_manager(); for (auto &n : nodes) { diff --git a/overlay/overlay-id.hpp b/overlay/overlay-id.hpp index c22b0faa7..2625773bd 100644 --- a/overlay/overlay-id.hpp +++ b/overlay/overlay-id.hpp @@ -21,8 +21,14 @@ #include "auto/tl/ton_api.h" #include "adnl/adnl-node-id.hpp" #include "overlay/overlays.h" +#include "td/utils/SharedSlice.h" +#include "td/utils/buffer.h" #include "td/utils/overloaded.h" #include "keys/encryptor.h" +#include "td/utils/port/StdStreams.h" +#include "td/utils/unique_ptr.h" +#include +#include namespace ton { @@ -30,18 +36,30 @@ namespace overlay { class OverlayNode { public: - explicit OverlayNode(adnl::AdnlNodeIdShort self_id, OverlayIdShort overlay) { + explicit OverlayNode(adnl::AdnlNodeIdShort self_id, OverlayIdShort overlay, td::uint32 flags) { source_ = self_id; overlay_ = overlay; + flags_ = flags; version_ = static_cast(td::Clocks::system()); } static td::Result create(const tl_object_ptr &node) { TRY_RESULT(source, adnl::AdnlNodeIdFull::create(node->id_)); - return OverlayNode{source, OverlayIdShort{node->overlay_}, node->version_, node->signature_.as_slice()}; + return OverlayNode{source, OverlayIdShort{node->overlay_}, 0, node->version_, node->signature_.as_slice()}; } - OverlayNode(td::Variant source, OverlayIdShort overlay, + static td::Result create(const tl_object_ptr &node) { + TRY_RESULT(source, adnl::AdnlNodeIdFull::create(node->id_)); + auto res = OverlayNode{source, OverlayIdShort{node->overlay_}, (td::uint32)node->flags_, node->version_, + node->signature_.as_slice()}; + res.update_certificate(OverlayMemberCertificate(node->certificate_.get())); + return res; + } + OverlayNode(td::Variant source, OverlayIdShort overlay, td::uint32 flags, td::int32 version, td::Slice signature) - : source_(std::move(source)), overlay_(overlay), version_(version), signature_(td::SharedSlice(signature)) { + : source_(std::move(source)) + , overlay_(overlay) + , flags_(flags) + , version_(version) + , signature_(td::SharedSlice(signature)) { } OverlayNode(td::Variant source, OverlayIdShort overlay, td::int32 version, td::SharedSlice signature) @@ -64,10 +82,17 @@ class OverlayNode { } td::BufferSlice to_sign() const { - auto obj = create_tl_object(nullptr, overlay_.tl(), version_); - source_.visit(td::overloaded([&](const adnl::AdnlNodeIdShort &id) { obj->id_ = id.tl(); }, - [&](const adnl::AdnlNodeIdFull &id) { obj->id_ = id.compute_short_id().tl(); })); - return serialize_tl_object(obj, true); + if (flags_ == 0) { + auto obj = create_tl_object(nullptr, overlay_.tl(), version_); + source_.visit(td::overloaded([&](const adnl::AdnlNodeIdShort &id) { obj->id_ = id.tl(); }, + [&](const adnl::AdnlNodeIdFull &id) { obj->id_ = id.compute_short_id().tl(); })); + return serialize_tl_object(obj, true); + } else { + auto obj = create_tl_object(nullptr, overlay_.tl(), flags_, version_); + source_.visit(td::overloaded([&](const adnl::AdnlNodeIdShort &id) { obj->id_ = id.tl(); }, + [&](const adnl::AdnlNodeIdFull &id) { obj->id_ = id.compute_short_id().tl(); })); + return serialize_tl_object(obj, true); + } } void update_adnl_id(adnl::AdnlNodeIdFull node_id) { source_ = node_id; @@ -81,6 +106,9 @@ class OverlayNode { td::int32 version() const { return version_; } + td::uint32 flags() const { + return flags_; + } td::BufferSlice signature() const { return signature_.clone_as_buffer_slice(); } @@ -103,15 +131,69 @@ class OverlayNode { [&](const adnl::AdnlNodeIdFull &id) { obj->id_ = id.tl(); })); return obj; } + tl_object_ptr tl_v2() const { + tl_object_ptr cert; + if (cert_ && !cert_->empty()) { + cert = cert_->tl(); + } else { + cert = create_tl_object(); + } + auto obj = create_tl_object(nullptr, overlay_.tl(), flags_, version_, + signature_.clone_as_buffer_slice(), std::move(cert)); + source_.visit(td::overloaded([&](const adnl::AdnlNodeIdShort &id) { UNREACHABLE(); }, + [&](const adnl::AdnlNodeIdFull &id) { obj->id_ = id.tl(); })); + return obj; + } OverlayNode clone() const { - return OverlayNode{source_, overlay_, version_, signature_.clone()}; + auto res = OverlayNode{source_, overlay_, version_, signature_.clone()}; + if (cert_) { + res.cert_ = td::make_unique(*cert_); + } + return res; + } + + const OverlayMemberCertificate *certificate() const { + if (cert_) { + return cert_.get(); + } + return &empty_certificate_; + } + + void update_certificate(OverlayMemberCertificate cert) { + if (!cert_ || cert_->empty() || cert_->is_expired() || cert.is_newer(*cert_)) { + cert_ = td::make_unique(std::move(cert)); + } + } + + void update(OverlayNode from) { + if (version_ < from.version_) { + source_ = from.source_; + overlay_ = from.overlay_; + flags_ = from.flags_; + version_ = from.version_; + signature_ = from.signature_.clone(); + } + if (from.cert_ && !from.cert_->empty()) { + update_certificate(std::move(*from.cert_)); + } + } + + void clear_certificate() { + cert_ = nullptr; + } + + bool has_full_id() const { + return source_.get_offset() == source_.offset(); } private: td::Variant source_; OverlayIdShort overlay_; + td::uint32 flags_; td::int32 version_; + td::unique_ptr cert_; td::SharedSlice signature_; + static const OverlayMemberCertificate empty_certificate_; }; } // namespace overlay diff --git a/overlay/overlay-manager.cpp b/overlay/overlay-manager.cpp index b9eb95b98..f062cc33b 100644 --- a/overlay/overlay-manager.cpp +++ b/overlay/overlay-manager.cpp @@ -18,6 +18,7 @@ */ #include "overlay-manager.h" #include "auto/tl/ton_api.h" +#include "auto/tl/ton_api.hpp" #include "overlay.h" #include "adnl/utils.hpp" @@ -28,9 +29,9 @@ #include "td/db/RocksDb.h" #include "td/utils/Status.h" +#include "td/utils/buffer.h" #include "td/utils/overloaded.h" -#include "keys/encryptor.h" #include "td/utils/port/Poll.h" #include @@ -42,13 +43,13 @@ void OverlayManager::update_dht_node(td::actor::ActorId dht) { dht_node_ = dht; for (auto &X : overlays_) { for (auto &Y : X.second) { - td::actor::send_closure(Y.second, &Overlay::update_dht_node, dht); + td::actor::send_closure(Y.second.overlay, &Overlay::update_dht_node, dht); } } } void OverlayManager::register_overlay(adnl::AdnlNodeIdShort local_id, OverlayIdShort overlay_id, - td::actor::ActorOwn overlay) { + OverlayMemberCertificate cert, td::actor::ActorOwn overlay) { auto it = overlays_.find(local_id); VLOG(OVERLAY_INFO) << this << ": registering overlay " << overlay_id << "@" << local_id; if (it == overlays_.end()) { @@ -58,19 +59,34 @@ void OverlayManager::register_overlay(adnl::AdnlNodeIdShort local_id, OverlayIdS td::actor::send_closure(adnl_, &adnl::Adnl::subscribe, local_id, adnl::Adnl::int_to_bytestring(ton_api::overlay_query::ID), std::make_unique(actor_id(this))); + td::actor::send_closure(adnl_, &adnl::Adnl::subscribe, local_id, + adnl::Adnl::int_to_bytestring(ton_api::overlay_messageWithExtra::ID), + std::make_unique(actor_id(this))); + td::actor::send_closure(adnl_, &adnl::Adnl::subscribe, local_id, + adnl::Adnl::int_to_bytestring(ton_api::overlay_queryWithExtra::ID), + std::make_unique(actor_id(this))); } - overlays_[local_id][overlay_id] = std::move(overlay); - - auto P = td::PromiseCreator::lambda([id = overlays_[local_id][overlay_id].get()](td::Result R) { - R.ensure(); - auto value = R.move_as_ok(); - if (value.status == td::KeyValue::GetStatus::Ok) { - auto F = fetch_tl_object(std::move(value.value), true); - F.ensure(); - auto nodes = std::move(F.move_as_ok()->nodes_); - td::actor::send_closure(id, &Overlay::receive_nodes_from_db, std::move(nodes)); - } - }); + overlays_[local_id][overlay_id] = OverlayDescription{std::move(overlay), std::move(cert)}; + + auto P = + td::PromiseCreator::lambda([id = overlays_[local_id][overlay_id].overlay.get()](td::Result R) { + R.ensure(); + auto value = R.move_as_ok(); + if (value.status == td::KeyValue::GetStatus::Ok) { + auto F = fetch_tl_object(std::move(value.value), true); + F.ensure(); + ton_api::downcast_call( + *F.move_as_ok(), td::overloaded( + [&](ton_api::overlay_db_nodes &V) { + auto nodes = std::move(V.nodes_); + td::actor::send_closure(id, &Overlay::receive_nodes_from_db, std::move(nodes)); + }, + [&](ton_api::overlay_db_nodesV2 &V) { + auto nodes = std::move(V.nodes_); + td::actor::send_closure(id, &Overlay::receive_nodes_from_db_v2, std::move(nodes)); + })); + } + }); auto key = create_hash_tl_object(local_id.bits256_value(), overlay_id.bits256_value()); db_.get(key, std::move(P)); } @@ -84,6 +100,10 @@ void OverlayManager::delete_overlay(adnl::AdnlNodeIdShort local_id, OverlayIdSho adnl::Adnl::int_to_bytestring(ton_api::overlay_message::ID)); td::actor::send_closure(adnl_, &adnl::Adnl::unsubscribe, local_id, adnl::Adnl::int_to_bytestring(ton_api::overlay_query::ID)); + td::actor::send_closure(adnl_, &adnl::Adnl::unsubscribe, local_id, + adnl::Adnl::int_to_bytestring(ton_api::overlay_messageWithExtra::ID)); + td::actor::send_closure(adnl_, &adnl::Adnl::unsubscribe, local_id, + adnl::Adnl::int_to_bytestring(ton_api::overlay_queryWithExtra::ID)); overlays_.erase(it); } } @@ -101,74 +121,120 @@ void OverlayManager::create_public_overlay_ex(adnl::AdnlNodeIdShort local_id, Ov td::string scope, OverlayOptions opts) { CHECK(!dht_node_.empty()); auto id = overlay_id.compute_short_id(); - register_overlay(local_id, id, - Overlay::create(keyring_, adnl_, actor_id(this), dht_node_, local_id, std::move(overlay_id), - std::move(callback), std::move(rules), scope, std::move(opts))); + register_overlay(local_id, id, OverlayMemberCertificate{}, + Overlay::create_public(keyring_, adnl_, actor_id(this), dht_node_, local_id, std::move(overlay_id), + std::move(callback), std::move(rules), scope, std::move(opts))); } void OverlayManager::create_private_overlay(adnl::AdnlNodeIdShort local_id, OverlayIdFull overlay_id, std::vector nodes, std::unique_ptr callback, OverlayPrivacyRules rules, std::string scope) { + create_private_overlay_ex(local_id, std::move(overlay_id), std::move(nodes), std::move(callback), std::move(rules), + std::move(scope), {}); +} + +void OverlayManager::create_private_overlay_ex(adnl::AdnlNodeIdShort local_id, OverlayIdFull overlay_id, + std::vector nodes, + std::unique_ptr callback, OverlayPrivacyRules rules, + std::string scope, OverlayOptions opts) { auto id = overlay_id.compute_short_id(); - register_overlay(local_id, id, - Overlay::create(keyring_, adnl_, actor_id(this), dht_node_, local_id, std::move(overlay_id), - std::move(nodes), std::move(callback), std::move(rules), std::move(scope))); + register_overlay(local_id, id, OverlayMemberCertificate{}, + Overlay::create_private(keyring_, adnl_, actor_id(this), dht_node_, local_id, std::move(overlay_id), + std::move(nodes), std::move(callback), std::move(rules), std::move(scope), + std::move(opts))); +} + +void OverlayManager::create_semiprivate_overlay(adnl::AdnlNodeIdShort local_id, OverlayIdFull overlay_id, + std::vector nodes, + std::vector root_public_keys, + OverlayMemberCertificate certificate, + std::unique_ptr callback, OverlayPrivacyRules rules, + td::string scope, OverlayOptions opts) { + auto id = overlay_id.compute_short_id(); + register_overlay( + local_id, id, certificate, + Overlay::create_semiprivate(keyring_, adnl_, actor_id(this), dht_node_, local_id, std::move(overlay_id), + std::move(nodes), std::move(root_public_keys), certificate, std::move(callback), + std::move(rules), std::move(scope), std::move(opts))); } void OverlayManager::receive_message(adnl::AdnlNodeIdShort src, adnl::AdnlNodeIdShort dst, td::BufferSlice data) { - auto R = fetch_tl_prefix(data, true); - - if (R.is_error()) { - VLOG(OVERLAY_WARNING) << this << ": can not parse overlay message: " << R.move_as_error(); - return; + OverlayIdShort overlay_id; + tl_object_ptr extra; + auto R = fetch_tl_prefix(data, true); + if (R.is_ok()) { + overlay_id = OverlayIdShort{R.ok()->overlay_}; + extra = std::move(R.ok()->extra_); + } else { + auto R2 = fetch_tl_prefix(data, true); + if (R2.is_ok()) { + overlay_id = OverlayIdShort{R2.ok()->overlay_}; + } else { + VLOG(OVERLAY_WARNING) << this << ": can not parse overlay message [" << src << "->" << dst + << "]: " << R2.move_as_error(); + return; + } } - auto M = R.move_as_ok(); - auto it = overlays_.find(dst); if (it == overlays_.end()) { - VLOG(OVERLAY_NOTICE) << this << ": message to unknown overlay " << M->overlay_ << "@" << dst; + VLOG(OVERLAY_NOTICE) << this << ": message to unknown overlay " << overlay_id << "@" << dst; return; } - auto it2 = it->second.find(OverlayIdShort{M->overlay_}); + auto it2 = it->second.find(overlay_id); if (it2 == it->second.end()) { - VLOG(OVERLAY_NOTICE) << this << ": message to localid is not in overlay " << M->overlay_ << "@" << dst; + VLOG(OVERLAY_NOTICE) << this << ": message to localid is not in overlay " << overlay_id << "@" << dst; return; } - td::actor::send_closure(it2->second, &Overlay::update_throughput_in_ctr, src, (td::uint32)data.size(), false); - td::actor::send_closure(it2->second, &Overlay::receive_message, src, std::move(data)); + td::actor::send_closure(it2->second.overlay, &Overlay::update_throughput_in_ctr, src, data.size(), false, false); + td::actor::send_closure(it2->second.overlay, &Overlay::receive_message, src, std::move(extra), std::move(data)); } void OverlayManager::receive_query(adnl::AdnlNodeIdShort src, adnl::AdnlNodeIdShort dst, td::BufferSlice data, td::Promise promise) { - auto R = fetch_tl_prefix(data, true); - - if (R.is_error()) { - VLOG(OVERLAY_WARNING) << this << ": can not parse overlay query [" << src << "->" << dst - << "]: " << R.move_as_error(); - promise.set_error(td::Status::Error(ErrorCode::protoviolation, "bad overlay query header")); - return; + OverlayIdShort overlay_id; + tl_object_ptr extra; + auto R = fetch_tl_prefix(data, true); + if (R.is_ok()) { + overlay_id = OverlayIdShort{R.ok()->overlay_}; + extra = std::move(R.ok()->extra_); + } else { + auto R2 = fetch_tl_prefix(data, true); + if (R2.is_ok()) { + overlay_id = OverlayIdShort{R2.ok()->overlay_}; + } else { + VLOG(OVERLAY_WARNING) << this << ": can not parse overlay query [" << src << "->" << dst + << "]: " << R2.move_as_error(); + promise.set_error(td::Status::Error(ErrorCode::protoviolation, "bad overlay query header")); + return; + } } - auto M = R.move_as_ok(); - auto it = overlays_.find(dst); if (it == overlays_.end()) { - VLOG(OVERLAY_NOTICE) << this << ": query to unknown overlay " << M->overlay_ << "@" << dst << " from " << src; + VLOG(OVERLAY_NOTICE) << this << ": query to unknown overlay " << overlay_id << "@" << dst << " from " << src; promise.set_error(td::Status::Error(ErrorCode::protoviolation, PSTRING() << "bad local_id " << dst)); return; } - auto it2 = it->second.find(OverlayIdShort{M->overlay_}); + auto it2 = it->second.find(overlay_id); if (it2 == it->second.end()) { - VLOG(OVERLAY_NOTICE) << this << ": query to localid not in overlay " << M->overlay_ << "@" << dst << " from " << src; - promise.set_error(td::Status::Error(ErrorCode::protoviolation, PSTRING() << "bad overlay_id " << M->overlay_)); + VLOG(OVERLAY_NOTICE) << this << ": query to localid not in overlay " << overlay_id << "@" << dst << " from " << src; + promise.set_error(td::Status::Error(ErrorCode::protoviolation, PSTRING() << "bad overlay_id " << overlay_id)); return; } - td::actor::send_closure(it2->second, &Overlay::update_throughput_in_ctr, src, (td::uint32)data.size(), true); - td::actor::send_closure(it2->second, &Overlay::receive_query, src, std::move(data), std::move(promise)); + td::actor::send_closure(it2->second.overlay, &Overlay::update_throughput_in_ctr, src, data.size(), true, false); + promise = [overlay = it2->second.overlay.get(), promise = std::move(promise), + src](td::Result R) mutable { + if (R.is_ok()) { + td::actor::send_closure(overlay, &Overlay::update_throughput_out_ctr, src, R.ok().size(), false, true); + } + promise.set_result(std::move(R)); + }; + td::actor::send_closure(it2->second.overlay, &Overlay::receive_query, src, std::move(extra), std::move(data), + std::move(promise)); } void OverlayManager::send_query_via(adnl::AdnlNodeIdShort dst, adnl::AdnlNodeIdShort src, OverlayIdShort overlay_id, @@ -176,35 +242,70 @@ void OverlayManager::send_query_via(adnl::AdnlNodeIdShort dst, adnl::AdnlNodeIdS td::BufferSlice query, td::uint64 max_answer_size, td::actor::ActorId via) { CHECK(query.size() <= adnl::Adnl::huge_packet_max_size()); - + + auto extra = create_tl_object(); + extra->flags_ = 0; + auto it = overlays_.find(src); if (it != overlays_.end()) { auto it2 = it->second.find(overlay_id); if (it2 != it->second.end()) { - td::actor::send_closure(it2->second, &Overlay::update_throughput_out_ctr, dst, (td::uint32)query.size(), true); + td::actor::send_closure(it2->second.overlay, &Overlay::update_throughput_out_ctr, dst, query.size(), true, false); + promise = [overlay = it2->second.overlay.get(), promise = std::move(promise), + dst](td::Result R) mutable { + if (R.is_ok()) { + td::actor::send_closure(overlay, &Overlay::update_throughput_in_ctr, dst, R.ok().size(), false, true); + } + promise.set_result(std::move(R)); + }; + if (!it2->second.member_certificate.empty()) { + extra->flags_ |= 1; + extra->certificate_ = it2->second.member_certificate.tl(); + } } } - - td::actor::send_closure( - via, &adnl::AdnlSenderInterface::send_query_ex, src, dst, std::move(name), std::move(promise), timeout, - create_serialize_tl_object_suffix(query.as_slice(), overlay_id.tl()), max_answer_size); + + auto extra_flags = extra->flags_; + td::BufferSlice serialized_query = + (extra_flags ? create_serialize_tl_object_suffix( + query.as_slice(), overlay_id.tl(), std::move(extra)) + : create_serialize_tl_object_suffix(query.as_slice(), overlay_id.tl())); + + td::actor::send_closure(via, &adnl::AdnlSenderInterface::send_query_ex, src, dst, std::move(name), std::move(promise), + timeout, std::move(serialized_query), max_answer_size); } void OverlayManager::send_message_via(adnl::AdnlNodeIdShort dst, adnl::AdnlNodeIdShort src, OverlayIdShort overlay_id, td::BufferSlice object, td::actor::ActorId via) { CHECK(object.size() <= adnl::Adnl::huge_packet_max_size()); - + + auto extra = create_tl_object(); + extra->flags_ = 0; + auto it = overlays_.find(src); if (it != overlays_.end()) { auto it2 = it->second.find(overlay_id); if (it2 != it->second.end()) { - td::actor::send_closure(it2->second, &Overlay::update_throughput_out_ctr, dst, (td::uint32)object.size(), false); + td::actor::send_closure(it2->second.overlay, &Overlay::update_throughput_out_ctr, dst, object.size(), false, + false); + if (!it2->second.member_certificate.empty()) { + // do not send certificate here, we hope that all our neighbours already know of out certificate + // we send it every second to some random nodes. Here we don't want to increase the size of the message + if (false) { + extra->flags_ |= 1; + extra->certificate_ = it2->second.member_certificate.tl(); + } + } } } - - td::actor::send_closure( - via, &adnl::AdnlSenderInterface::send_message, src, dst, - create_serialize_tl_object_suffix(object.as_slice(), overlay_id.tl())); + + auto extra_flags = extra->flags_; + td::BufferSlice serialized_message = + (extra_flags ? create_serialize_tl_object_suffix( + object.as_slice(), overlay_id.tl(), std::move(extra)) + : create_serialize_tl_object_suffix(object.as_slice(), overlay_id.tl())); + + td::actor::send_closure(via, &adnl::AdnlSenderInterface::send_message, src, dst, std::move(serialized_message)); } void OverlayManager::send_broadcast(adnl::AdnlNodeIdShort local_id, OverlayIdShort overlay_id, td::BufferSlice object) { @@ -218,7 +319,7 @@ void OverlayManager::send_broadcast_ex(adnl::AdnlNodeIdShort local_id, OverlayId if (it != overlays_.end()) { auto it2 = it->second.find(overlay_id); if (it2 != it->second.end()) { - td::actor::send_closure(it2->second, &Overlay::send_broadcast, send_as, flags, std::move(object)); + td::actor::send_closure(it2->second.overlay, &Overlay::send_broadcast, send_as, flags, std::move(object)); } } } @@ -235,7 +336,7 @@ void OverlayManager::send_broadcast_fec_ex(adnl::AdnlNodeIdShort local_id, Overl if (it != overlays_.end()) { auto it2 = it->second.find(overlay_id); if (it2 != it->second.end()) { - td::actor::send_closure(it2->second, &Overlay::send_broadcast_fec, send_as, flags, std::move(object)); + td::actor::send_closure(it2->second.overlay, &Overlay::send_broadcast_fec, send_as, flags, std::move(object)); } } } @@ -246,7 +347,7 @@ void OverlayManager::set_privacy_rules(adnl::AdnlNodeIdShort local_id, OverlayId if (it != overlays_.end()) { auto it2 = it->second.find(overlay_id); if (it2 != it->second.end()) { - td::actor::send_closure(it2->second, &Overlay::set_privacy_rules, std::move(rules)); + td::actor::send_closure(it2->second.overlay, &Overlay::set_privacy_rules, std::move(rules)); } } } @@ -257,23 +358,54 @@ void OverlayManager::update_certificate(adnl::AdnlNodeIdShort local_id, OverlayI if (it != overlays_.end()) { auto it2 = it->second.find(overlay_id); if (it2 != it->second.end()) { - td::actor::send_closure(it2->second, &Overlay::add_certificate, key, std::move(cert)); + td::actor::send_closure(it2->second.overlay, &Overlay::add_certificate, key, std::move(cert)); } } } -void OverlayManager::get_overlay_random_peers(adnl::AdnlNodeIdShort local_id, OverlayIdShort overlay_id, - td::uint32 max_peers, - td::Promise> promise) { +void OverlayManager::update_member_certificate(adnl::AdnlNodeIdShort local_id, OverlayIdShort overlay_id, + OverlayMemberCertificate certificate) { auto it = overlays_.find(local_id); if (it != overlays_.end()) { auto it2 = it->second.find(overlay_id); if (it2 != it->second.end()) { - td::actor::send_closure(it2->second, &Overlay::get_overlay_random_peers, max_peers, std::move(promise)); + it2->second.member_certificate = certificate; + td::actor::send_closure(it2->second.overlay, &Overlay::update_member_certificate, certificate); } } } +void OverlayManager::update_root_member_list(adnl::AdnlNodeIdShort local_id, OverlayIdShort overlay_id, + std::vector nodes, + std::vector root_public_keys, + OverlayMemberCertificate certificate) { + auto it = overlays_.find(local_id); + if (it != overlays_.end()) { + auto it2 = it->second.find(overlay_id); + if (it2 != it->second.end()) { + it2->second.member_certificate = certificate; + td::actor::send_closure(it2->second.overlay, &Overlay::update_root_member_list, std::move(nodes), + std::move(root_public_keys), std::move(certificate)); + } + } +} + +void OverlayManager::get_overlay_random_peers(adnl::AdnlNodeIdShort local_id, OverlayIdShort overlay_id, + td::uint32 max_peers, + td::Promise> promise) { + auto it = overlays_.find(local_id); + if (it == overlays_.end()) { + promise.set_error(td::Status::Error(PSTRING() << "no such local id " << local_id)); + return; + } + auto it2 = it->second.find(overlay_id); + if (it2 == it->second.end()) { + promise.set_error(td::Status::Error(PSTRING() << "no such overlay " << overlay_id)); + return; + } + td::actor::send_closure(it2->second.overlay, &Overlay::get_overlay_random_peers, max_peers, std::move(promise)); +} + td::actor::ActorOwn Overlays::create(std::string db_root, td::actor::ActorId keyring, td::actor::ActorId adnl, td::actor::ActorId dht) { return td::actor::create_actor("overlaymanager", db_root, keyring, adnl, dht); @@ -334,7 +466,7 @@ void OverlayManager::get_stats(td::Promise> R) { if (R.is_ok()) { td::actor::send_closure(act, &Cb::receive_answer, R.move_as_ok()); @@ -348,6 +480,19 @@ void OverlayManager::get_stats(td::Promisesecond.find(overlay); + if (it2 == it->second.end()) { + return; + } + td::actor::send_closure(it2->second.overlay, &Overlay::forget_peer, peer_id); +} + Certificate::Certificate(PublicKey issued_by, td::int32 expire_at, td::uint32 max_size, td::uint32 flags, td::BufferSlice signature) : issued_by_(issued_by) @@ -454,6 +599,35 @@ tl_object_ptr Certificate::empty_tl() { return create_tl_object(); } +OverlayMemberCertificate::OverlayMemberCertificate(const ton_api::overlay_MemberCertificate *cert) { + if (!cert) { + expire_at_ = std::numeric_limits::max(); + return; + } + if (cert->get_id() == ton_api::overlay_emptyMemberCertificate::ID) { + expire_at_ = std::numeric_limits::max(); + return; + } + CHECK(cert->get_id() == ton_api::overlay_memberCertificate::ID); + const auto *real_cert = static_cast(cert); + signed_by_ = PublicKey(real_cert->issued_by_); + flags_ = real_cert->flags_; + slot_ = real_cert->slot_; + expire_at_ = real_cert->expire_at_; + signature_ = td::SharedSlice(real_cert->signature_.as_slice()); +} + +td::Status OverlayMemberCertificate::check_signature(const adnl::AdnlNodeIdShort &node) { + if (is_expired()) { + return td::Status::Error(ErrorCode::notready, "certificate is expired"); + } + td::BufferSlice data_to_sign = to_sign_data(node); + + TRY_RESULT(encryptor, signed_by_.create_encryptor()); + TRY_STATUS(encryptor->check_signature(data_to_sign.as_slice(), signature_.as_slice())); + return td::Status::OK(); +} + } // namespace overlay } // namespace ton diff --git a/overlay/overlay-manager.h b/overlay/overlay-manager.h index 1b9c75a4e..12206e048 100644 --- a/overlay/overlay-manager.h +++ b/overlay/overlay-manager.h @@ -53,11 +53,19 @@ class OverlayManager : public Overlays { void create_public_overlay(adnl::AdnlNodeIdShort local_id, OverlayIdFull overlay_id, std::unique_ptr callback, OverlayPrivacyRules rules, td::string scope) override; void create_public_overlay_ex(adnl::AdnlNodeIdShort local_id, OverlayIdFull overlay_id, - std::unique_ptr callback, OverlayPrivacyRules rules, td::string scope, - OverlayOptions opts) override; + std::unique_ptr callback, OverlayPrivacyRules rules, td::string scope, + OverlayOptions opts) override; + void create_semiprivate_overlay(adnl::AdnlNodeIdShort local_id, OverlayIdFull overlay_id, + std::vector nodes, std::vector root_public_keys, + OverlayMemberCertificate certificate, + std::unique_ptr callback, OverlayPrivacyRules rules, td::string scope, + OverlayOptions opts) override; void create_private_overlay(adnl::AdnlNodeIdShort local_id, OverlayIdFull overlay_id, std::vector nodes, std::unique_ptr callback, OverlayPrivacyRules rules, std::string scope) override; + void create_private_overlay_ex(adnl::AdnlNodeIdShort local_id, OverlayIdFull overlay_id, + std::vector nodes, std::unique_ptr callback, + OverlayPrivacyRules rules, std::string scope, OverlayOptions opts) override; void delete_overlay(adnl::AdnlNodeIdShort local_id, OverlayIdShort overlay_id) override; void send_query(adnl::AdnlNodeIdShort dst, adnl::AdnlNodeIdShort src, OverlayIdShort overlay_id, std::string name, td::Promise promise, td::Timestamp timeout, td::BufferSlice query) override { @@ -84,6 +92,11 @@ class OverlayManager : public Overlays { void set_privacy_rules(adnl::AdnlNodeIdShort local_id, OverlayIdShort overlay_id, OverlayPrivacyRules rules) override; void update_certificate(adnl::AdnlNodeIdShort local_id, OverlayIdShort overlay_id, PublicKeyHash key, std::shared_ptr cert) override; + void update_member_certificate(adnl::AdnlNodeIdShort local_id, OverlayIdShort overlay_id, + OverlayMemberCertificate certificate) override; + void update_root_member_list(adnl::AdnlNodeIdShort local_id, OverlayIdShort overlay_id, + std::vector nodes, std::vector root_public_keys, + OverlayMemberCertificate certificate) override; void get_overlay_random_peers(adnl::AdnlNodeIdShort local_id, OverlayIdShort overlay, td::uint32 max_peers, td::Promise> promise) override; @@ -92,10 +105,12 @@ class OverlayManager : public Overlays { td::Promise promise); void receive_message(adnl::AdnlNodeIdShort src, adnl::AdnlNodeIdShort dst, td::BufferSlice data); - void register_overlay(adnl::AdnlNodeIdShort local_id, OverlayIdShort overlay_id, + void register_overlay(adnl::AdnlNodeIdShort local_id, OverlayIdShort overlay_id, OverlayMemberCertificate cert, td::actor::ActorOwn overlay); void get_stats(td::Promise> promise) override; + void forget_peer(adnl::AdnlNodeIdShort local_id, OverlayIdShort overlay, adnl::AdnlNodeIdShort peer_id) override; + struct PrintId {}; PrintId print_id() const { @@ -103,7 +118,11 @@ class OverlayManager : public Overlays { } private: - std::map>> overlays_; + struct OverlayDescription { + td::actor::ActorOwn overlay; + OverlayMemberCertificate member_certificate; + }; + std::map> overlays_; std::string db_root_; diff --git a/overlay/overlay-peers.cpp b/overlay/overlay-peers.cpp index e81fecc70..aba62eb15 100644 --- a/overlay/overlay-peers.cpp +++ b/overlay/overlay-peers.cpp @@ -16,89 +16,165 @@ Copyright 2017-2020 Telegram Systems LLP */ +#include "adnl/adnl-node-id.hpp" +#include "adnl/adnl-node.h" +#include "auto/tl/ton_api.h" #include "overlay.hpp" +#include "td/utils/Status.h" +#include "td/utils/Time.h" +#include "td/utils/port/signals.h" +#include +#include namespace ton { namespace overlay { -void OverlayImpl::del_peer(adnl::AdnlNodeIdShort id) { - auto P = peers_.get(id); - CHECK(P != nullptr); +void OverlayImpl::del_peer(const adnl::AdnlNodeIdShort &id) { + auto P = peer_list_.peers_.get(id); + if (P == nullptr) { + return; + } + if (P->is_permanent_member()) { + VLOG(OVERLAY_DEBUG) << this << ": not deleting peer " << id << ": a permanent member"; + return; + } VLOG(OVERLAY_DEBUG) << this << ": deleting peer " << id; if (P->is_neighbour()) { - VLOG(OVERLAY_INFO) << this << ": deleting neighbour " << id; - bool deleted = false; - for (auto &n : neighbours_) { - if (n == id) { - n = neighbours_[neighbours_.size() - 1]; - neighbours_.resize(neighbours_.size() - 1); - deleted = true; - break; - } + del_from_neighbour_list(P); + } + peer_list_.peers_.remove(id); + peer_list_.bad_peers_.erase(id); +} + +void OverlayImpl::del_from_neighbour_list(OverlayPeer *P) { + CHECK(P); + if (!P->is_neighbour()) { + return; + } + auto id = P->get_id(); + bool deleted = false; + auto &neighbours = peer_list_.neighbours_; + for (auto &n : neighbours) { + if (n == id) { + n = neighbours[neighbours.size() - 1]; + neighbours.resize(neighbours.size() - 1); + deleted = true; + break; } - CHECK(deleted); - P->set_neighbour(false); } - peers_.remove(id); - bad_peers_.erase(id); - update_neighbours(0); + CHECK(deleted); + P->set_neighbour(false); +} + +void OverlayImpl::del_from_neighbour_list(const adnl::AdnlNodeIdShort &id) { + auto P = peer_list_.peers_.get(id); + CHECK(P != nullptr); + return del_from_neighbour_list(P); } void OverlayImpl::del_some_peers() { - if (!public_) { + if (overlay_type_ == OverlayType::FixedMemberList) { return; } - while (peers_.size() > max_peers()) { + const size_t max_iterations = 10; + size_t iteration_seqno = 0; + while (peer_list_.peers_.size() > max_peers() && iteration_seqno++ < max_iterations) { OverlayPeer *P; - if (bad_peers_.empty()) { + if (peer_list_.bad_peers_.empty()) { P = get_random_peer(); } else { - auto it = bad_peers_.upper_bound(next_bad_peer_); - if (it == bad_peers_.end()) { - it = bad_peers_.begin(); + auto it = peer_list_.bad_peers_.upper_bound(peer_list_.next_bad_peer_); + if (it == peer_list_.bad_peers_.end()) { + it = peer_list_.bad_peers_.begin(); } - P = peers_.get(next_bad_peer_ = *it); + P = peer_list_.peers_.get(peer_list_.next_bad_peer_ = *it); } - if (P) { + if (P && !P->is_permanent_member()) { auto id = P->get_id(); del_peer(id); } } + update_neighbours(0); } -void OverlayImpl::do_add_peer(OverlayNode node) { - auto id = node.adnl_id_short(); - - auto V = peers_.get(id); - if (V) { - VLOG(OVERLAY_DEBUG) << this << ": updating peer " << id << " up to version " << node.version(); - V->update(std::move(node)); - } else { - VLOG(OVERLAY_DEBUG) << this << ": adding peer " << id << " of version " << node.version(); - peers_.insert(id, OverlayPeer(std::move(node))); - - del_some_peers(); - update_neighbours(0); +td::Status OverlayImpl::validate_peer_certificate(const adnl::AdnlNodeIdShort &node, + const OverlayMemberCertificate &cert) { + if (cert.empty()) { + if (is_persistent_node(node) || overlay_type_ == OverlayType::Public) { + return td::Status::OK(); + } + return td::Status::Error(ErrorCode::protoviolation, "no member certificate found"); + } + if (cert.is_expired()) { + return td::Status::Error(ErrorCode::timeout, "member certificate is expired"); + } + if (cert.slot() < 0 || cert.slot() >= opts_.max_slaves_in_semiprivate_overlay_) { + return td::Status::Error(ErrorCode::timeout, "member certificate has invalid slot"); + } + const auto &issued_by = cert.issued_by(); + auto it = peer_list_.root_public_keys_.find(issued_by.compute_short_id()); + if (it == peer_list_.root_public_keys_.end()) { + return td::Status::Error(ErrorCode::protoviolation, "member certificate is signed by unknown public key"); + } + if (it->second.size() > (size_t)cert.slot()) { + auto &el = it->second[cert.slot()]; + if (cert.expire_at() < el.expire_at) { + return td::Status::Error(ErrorCode::protoviolation, + "member certificate rejected, because we know of newer certificate at the same slot"); + } else if (cert.expire_at() == el.expire_at) { + if (node < el.node) { + return td::Status::Error(ErrorCode::protoviolation, + "member certificate rejected, because we know of newer certificate at the same slot"); + } else if (el.node == node) { + // we could return OK here, but we must make sure, that the unchecked signature will not be used for updating PeerNode. + } + } + } + auto R = get_encryptor(issued_by); + if (R.is_error()) { + return R.move_as_error_prefix("failed to check member certificate: failed to create encryptor: "); + } + auto enc = R.move_as_ok(); + auto S = enc->check_signature(cert.to_sign_data(node).as_slice(), cert.signature()); + if (S.is_error()) { + return S.move_as_error_prefix("failed to check member certificate: bad signature: "); + } + if (it->second.size() <= (size_t)cert.slot()) { + it->second.resize((size_t)cert.slot() + 1); } + it->second[cert.slot()].expire_at = cert.expire_at(); + it->second[cert.slot()].node = node; + return td::Status::OK(); } -void OverlayImpl::add_peer_in_cont(OverlayNode node) { - CHECK(public_); +td::Status OverlayImpl::validate_peer_certificate(const adnl::AdnlNodeIdShort &node, + ton_api::overlay_MemberCertificate *cert) { + OverlayMemberCertificate ncert(cert); + return validate_peer_certificate(node, ncert); +} - do_add_peer(std::move(node)); +td::Status OverlayImpl::validate_peer_certificate(const adnl::AdnlNodeIdShort &node, + const OverlayMemberCertificate *cert) { + if (!cert) { + if (is_persistent_node(node) || overlay_type_ == OverlayType::Public) { + return td::Status::OK(); + } + return td::Status::Error(ErrorCode::protoviolation, "no member certificate found"); + } + return validate_peer_certificate(node, *cert); } -void OverlayImpl::add_peer_in(OverlayNode node) { - CHECK(public_); +void OverlayImpl::add_peer(OverlayNode node) { + CHECK(overlay_type_ != OverlayType::FixedMemberList); if (node.overlay_id() != overlay_id_) { VLOG(OVERLAY_WARNING) << this << ": received node with bad overlay"; return; } auto t = td::Clocks::system(); - if (node.version() + 600 < t || node.version() > t + 60) { + if (node.version() + Overlays::overlay_peer_ttl() < t || node.version() > t + 60) { VLOG(OVERLAY_INFO) << this << ": ignoring node of too old version " << node.version(); return; } @@ -115,35 +191,78 @@ void OverlayImpl::add_peer_in(OverlayNode node) { return; } - add_peer_in_cont(std::move(node)); + if (overlay_type_ == OverlayType::CertificatedMembers) { + auto R = validate_peer_certificate(node.adnl_id_short(), *node.certificate()); + if (R.is_error()) { + VLOG(OVERLAY_WARNING) << this << ": bad peer certificate node=" << node.adnl_id_short() << ": " + << R.move_as_error(); + UNREACHABLE(); + return; + } + } + + auto id = node.adnl_id_short(); + + auto V = peer_list_.peers_.get(id); + if (V) { + VLOG(OVERLAY_DEBUG) << this << ": updating peer " << id << " up to version " << node.version(); + V->update(std::move(node)); + } else { + VLOG(OVERLAY_DEBUG) << this << ": adding peer " << id << " of version " << node.version(); + CHECK(overlay_type_ != OverlayType::CertificatedMembers || (node.certificate() && !node.certificate()->empty())); + peer_list_.peers_.insert(id, OverlayPeer(std::move(node))); + del_some_peers(); + auto X = peer_list_.peers_.get(id); + if (X != nullptr && peer_list_.neighbours_.size() < max_neighbours() && + !(X->get_node()->flags() & OverlayMemberFlags::DoNotReceiveBroadcasts) && X->get_id() != local_id_) { + peer_list_.neighbours_.push_back(X->get_id()); + X->set_neighbour(true); + } + + update_neighbours(0); + } } void OverlayImpl::add_peers(std::vector peers) { for (auto &node : peers) { - add_peer_in(std::move(node)); + add_peer(std::move(node)); } } -void OverlayImpl::add_peer(OverlayNode P) { - add_peer_in(std::move(P)); +void OverlayImpl::add_peers(const tl_object_ptr &nodes) { + for (auto &n : nodes->nodes_) { + auto N = OverlayNode::create(n); + if (N.is_ok()) { + add_peer(N.move_as_ok()); + } + } +} + +void OverlayImpl::add_peers(const tl_object_ptr &nodes) { + for (auto &n : nodes->nodes_) { + auto N = OverlayNode::create(n); + if (N.is_ok()) { + add_peer(N.move_as_ok()); + } + } } void OverlayImpl::on_ping_result(adnl::AdnlNodeIdShort peer, bool success) { - if (!public_) { + if (overlay_type_ == OverlayType::FixedMemberList) { return; } - if (OverlayPeer *p = peers_.get(peer)) { + if (OverlayPeer *p = peer_list_.peers_.get(peer)) { p->on_ping_result(success); if (p->is_alive()) { - bad_peers_.erase(peer); + peer_list_.bad_peers_.erase(peer); } else { - bad_peers_.insert(peer); + peer_list_.bad_peers_.insert(peer); } } } void OverlayImpl::receive_random_peers(adnl::AdnlNodeIdShort src, td::Result R) { - CHECK(public_); + CHECK(overlay_type_ != OverlayType::FixedMemberList); on_ping_result(src, R.is_ok()); if (R.is_error()) { VLOG(OVERLAY_NOTICE) << this << ": failed getRandomPeers query: " << R.move_as_error(); @@ -156,16 +275,24 @@ void OverlayImpl::receive_random_peers(adnl::AdnlNodeIdShort src, td::Result nodes; - for (auto &n : res->nodes_) { - auto N = OverlayNode::create(n); - if (N.is_ok()) { - nodes.emplace_back(N.move_as_ok()); - } +void OverlayImpl::receive_random_peers_v2(adnl::AdnlNodeIdShort src, td::Result R) { + CHECK(overlay_type_ != OverlayType::FixedMemberList); + on_ping_result(src, R.is_ok()); + if (R.is_error()) { + VLOG(OVERLAY_NOTICE) << this << ": failed getRandomPeersV2 query: " << R.move_as_error(); + return; } - add_peers(std::move(nodes)); + auto R2 = fetch_tl_object(R.move_as_ok(), true); + if (R2.is_error()) { + VLOG(OVERLAY_WARNING) << this << ": dropping incorrect answer to overlay.getRandomPeers query from " << src << ": " + << R2.move_as_error(); + return; + } + + add_peers(R2.move_as_ok()); } void OverlayImpl::send_random_peers_cont(adnl::AdnlNodeIdShort src, OverlayNode node, @@ -175,10 +302,13 @@ void OverlayImpl::send_random_peers_cont(adnl::AdnlNodeIdShort src, OverlayNode vec.emplace_back(node.tl()); } - for (td::uint32 i = 0; i < nodes_to_send(); i++) { + td::uint32 max_iterations = nodes_to_send() + 16; + for (td::uint32 i = 0; i < max_iterations && vec.size() < nodes_to_send(); i++) { auto P = get_random_peer(true); if (P) { - vec.emplace_back(P->get().tl()); + if (P->has_full_id()) { + vec.emplace_back(P->get_node()->tl()); + } } else { break; } @@ -213,58 +343,110 @@ void OverlayImpl::send_random_peers(adnl::AdnlNodeIdShort src, td::Promise promise) { + std::vector> vec; + if (announce_self_) { + CHECK(is_persistent_node(node.adnl_id_short()) || !node.certificate()->empty()); + vec.emplace_back(node.tl_v2()); + } + + td::uint32 max_iterations = nodes_to_send() + 16; + for (td::uint32 i = 0; i < max_iterations && vec.size() < nodes_to_send(); i++) { + auto P = get_random_peer(true); + if (P) { + if (P->has_full_id() && !P->is_permanent_member()) { + vec.emplace_back(P->get_node()->tl_v2()); + } + } else { + break; + } + } + + if (promise) { + auto Q = create_tl_object(std::move(vec)); + promise.set_value(serialize_tl_object(Q, true)); + } else { + auto P = + td::PromiseCreator::lambda([SelfId = actor_id(this), src, oid = print_id()](td::Result res) { + td::actor::send_closure(SelfId, &OverlayImpl::receive_random_peers_v2, src, std::move(res)); + }); + auto Q = + create_tl_object(create_tl_object(std::move(vec))); + td::actor::send_closure(manager_, &OverlayManager::send_query, src, local_id_, overlay_id_, + "overlay getRandomPeers", std::move(P), + td::Timestamp::in(5.0 + td::Random::fast(0, 50) * 0.1), serialize_tl_object(Q, true)); + } +} + +void OverlayImpl::send_random_peers_v2(adnl::AdnlNodeIdShort src, td::Promise promise) { + auto P = td::PromiseCreator::lambda([src, promise = std::move(promise), + SelfId = actor_id(this)](td::Result res) mutable { + if (res.is_error()) { + promise.set_error(td::Status::Error(ErrorCode::error, "cannot get self node")); + return; + } + td::actor::send_closure(SelfId, &OverlayImpl::send_random_peers_v2_cont, src, res.move_as_ok(), std::move(promise)); + }); + + get_self_node(std::move(P)); +} + void OverlayImpl::update_neighbours(td::uint32 nodes_to_change) { - if (peers_.size() == 0) { + if (peer_list_.peers_.size() == 0) { return; } td::uint32 iter = 0; - while (iter < 10 && (nodes_to_change > 0 || neighbours_.size() < max_neighbours())) { - auto X = peers_.get_random(); + while (iter++ < 10 && (nodes_to_change > 0 || peer_list_.neighbours_.size() < max_neighbours())) { + auto X = peer_list_.peers_.get_random(); if (!X) { break; } if (X->get_id() == local_id_) { - iter++; continue; } - if (public_ && X->get_version() <= td::Clocks::system() - 600) { + if (X->get_version() <= td::Clocks::system() - Overlays::overlay_peer_ttl()) { + if (X->is_permanent_member()) { + del_from_neighbour_list(X); + } else { + auto id = X->get_id(); + del_peer(id); + } + continue; + } + + if (overlay_type_ == OverlayType::CertificatedMembers && !X->is_permanent_member() && + X->certificate()->is_expired()) { + auto id = X->get_id(); + del_peer(id); + continue; + } + + if (X->get_node()->flags() & OverlayMemberFlags::DoNotReceiveBroadcasts) { if (X->is_neighbour()) { - bool found = false; - for (auto &n : neighbours_) { - if (n == X->get_id()) { - n = *neighbours_.rbegin(); - found = true; - break; - } - } - CHECK(found); - neighbours_.pop_back(); - X->set_neighbour(false); + del_from_neighbour_list(X); } - bad_peers_.erase(X->get_id()); - peers_.remove(X->get_id()); continue; } if (X->is_neighbour()) { - iter++; continue; } - if (neighbours_.size() < max_neighbours()) { + if (peer_list_.neighbours_.size() < max_neighbours()) { VLOG(OVERLAY_INFO) << this << ": adding new neighbour " << X->get_id(); - neighbours_.push_back(X->get_id()); + peer_list_.neighbours_.push_back(X->get_id()); X->set_neighbour(true); } else { CHECK(nodes_to_change > 0); - auto i = td::Random::fast(0, static_cast(neighbours_.size()) - 1); - auto Y = peers_.get(neighbours_[i]); + auto i = td::Random::fast(0, static_cast(peer_list_.neighbours_.size()) - 1); + auto Y = peer_list_.peers_.get(peer_list_.neighbours_[i]); CHECK(Y != nullptr); CHECK(Y->is_neighbour()); Y->set_neighbour(false); - neighbours_[i] = X->get_id(); + peer_list_.neighbours_[i] = X->get_id(); X->set_neighbour(true); nodes_to_change--; VLOG(OVERLAY_INFO) << this << ": changing neighbour " << Y->get_id() << " -> " << X->get_id(); @@ -274,9 +456,11 @@ void OverlayImpl::update_neighbours(td::uint32 nodes_to_change) { OverlayPeer *OverlayImpl::get_random_peer(bool only_alive) { size_t skip_bad = 3; - while (peers_.size() > (only_alive ? bad_peers_.size() : 0)) { - auto P = peers_.get_random(); - if (public_ && P->get_version() + 3600 < td::Clocks::system()) { + OverlayPeer *res = nullptr; + while (!res && peer_list_.peers_.size() > (only_alive ? peer_list_.bad_peers_.size() : 0)) { + auto P = peer_list_.peers_.get_random(); + if (!P->is_permanent_member() && + (P->get_version() + 3600 < td::Clocks::system() || P->certificate()->is_expired())) { VLOG(OVERLAY_INFO) << this << ": deleting outdated peer " << P->get_id(); del_peer(P->get_id()); continue; @@ -290,18 +474,19 @@ OverlayPeer *OverlayImpl::get_random_peer(bool only_alive) { continue; } } - return P; + res = P; } - return nullptr; + update_neighbours(0); + return res; } void OverlayImpl::get_overlay_random_peers(td::uint32 max_peers, td::Promise> promise) { std::vector v; auto t = td::Clocks::system(); - while (v.size() < max_peers && v.size() < peers_.size() - bad_peers_.size()) { - auto P = peers_.get_random(); - if (public_ && P->get_version() + 3600 < t) { + while (v.size() < max_peers && v.size() < peer_list_.peers_.size() - peer_list_.bad_peers_.size()) { + auto P = peer_list_.peers_.get_random(); + if (!P->is_permanent_member() && (P->get_version() + 3600 < t || P->certificate()->is_expired(t))) { VLOG(OVERLAY_INFO) << this << ": deleting outdated peer " << P->get_id(); del_peer(P->get_id()); } else if (P->is_alive()) { @@ -317,22 +502,234 @@ void OverlayImpl::get_overlay_random_peers(td::uint32 max_peers, } } } + update_neighbours(0); promise.set_result(std::move(v)); } void OverlayImpl::receive_nodes_from_db(tl_object_ptr tl_nodes) { - if (public_) { - std::vector nodes; - for (auto &n : tl_nodes->nodes_) { - auto N = OverlayNode::create(n); - if (N.is_ok()) { - nodes.emplace_back(N.move_as_ok()); + if (overlay_type_ != OverlayType::FixedMemberList) { + add_peers(tl_nodes); + } +} + +void OverlayImpl::receive_nodes_from_db_v2(tl_object_ptr tl_nodes) { + if (overlay_type_ != OverlayType::FixedMemberList) { + add_peers(tl_nodes); + } +} + +bool OverlayImpl::is_persistent_node(const adnl::AdnlNodeIdShort &id) { + auto P = peer_list_.peers_.get(id); + if (!P) { + return false; + } + return P->is_permanent_member(); +} + +bool OverlayImpl::is_valid_peer(const adnl::AdnlNodeIdShort &src, + const ton_api::overlay_MemberCertificate *certificate) { + if (overlay_type_ == OverlayType::Public) { + on_ping_result(src, true); + return true; + } else if (overlay_type_ == OverlayType::FixedMemberList) { + return peer_list_.peers_.get(src); + } else { + OverlayMemberCertificate cert(certificate); + if (cert.empty()) { + auto P = peer_list_.peers_.get(src); + if (P && !P->is_permanent_member()) { + auto C = P->certificate(); + if (C) { + cert = *C; + } } } - add_peers(std::move(nodes)); + + auto S = validate_peer_certificate(src, cert); + if (S.is_error()) { + VLOG(OVERLAY_WARNING) << "adnl=" << src << ": certificate is invalid: " << S; + return false; + } + auto P = peer_list_.peers_.get(src); + if (P) { + CHECK(P->is_permanent_member() || !cert.empty()); + P->update_certificate(std::move(cert)); + } + return true; + } +} + +void OverlayImpl::iterate_all_peers(std::function cb) { + peer_list_.peers_.iterate([&](const adnl::AdnlNodeIdShort &key, OverlayPeer &peer) { cb(key, peer); }); +} + +void OverlayImpl::update_peer_err_ctr(adnl::AdnlNodeIdShort peer_id, bool is_fec) { + auto src_peer = peer_list_.peers_.get(peer_id); + if (src_peer) { + if (is_fec) { + src_peer->fec_broadcast_errors++; + } else { + src_peer->broadcast_errors++; + } } } +void OverlayImpl::update_throughput_out_ctr(adnl::AdnlNodeIdShort peer_id, td::uint64 msg_size, bool is_query, + bool is_response) { + auto out_peer = peer_list_.peers_.get(peer_id); + if (out_peer) { + out_peer->traffic_ctr.add_packet(msg_size, false); + if (is_response) { + out_peer->traffic_responses_ctr.add_packet(msg_size, false); + } + if (is_query) { + out_peer->last_out_query_at = td::Timestamp::now(); + } + } + total_traffic_ctr.add_packet(msg_size, false); + if (is_response) { + total_traffic_responses_ctr.add_packet(msg_size, false); + } +} + +void OverlayImpl::update_throughput_in_ctr(adnl::AdnlNodeIdShort peer_id, td::uint64 msg_size, bool is_query, + bool is_response) { + auto in_peer = peer_list_.peers_.get(peer_id); + if (in_peer) { + in_peer->traffic_ctr.add_packet(msg_size, true); + if (is_response) { + in_peer->traffic_responses_ctr.add_packet(msg_size, true); + } + if (is_query) { + in_peer->last_in_query_at = td::Timestamp::now(); + } + } + total_traffic_ctr.add_packet(msg_size, true); + if (is_response) { + total_traffic_responses_ctr.add_packet(msg_size, true); + } +} + +void OverlayImpl::update_peer_ip_str(adnl::AdnlNodeIdShort peer_id, td::string ip_str) { + auto fpeer = peer_list_.peers_.get(peer_id); + if (fpeer) { + fpeer->ip_addr_str = ip_str; + } +} + +bool OverlayImpl::has_good_peers() const { + return peer_list_.peers_.size() > peer_list_.bad_peers_.size(); +} + +bool OverlayImpl::is_root_public_key(const PublicKeyHash &key) const { + return peer_list_.root_public_keys_.count(key) > 0; +} + +std::vector OverlayImpl::get_neighbours(td::uint32 max_size) const { + if (max_size == 0 || max_size >= peer_list_.neighbours_.size()) { + return peer_list_.neighbours_; + } else { + std::vector vec; + std::vector ul; + for (td::uint32 i = 0; i < max_size; i++) { + td::uint32 t = td::Random::fast(0, static_cast(peer_list_.neighbours_.size()) - 1 - i); + td::uint32 j; + for (j = 0; j < i && ul[j] <= t; j++) { + t++; + } + ul.emplace(ul.begin() + j, t); + vec.push_back(peer_list_.neighbours_[t]); + } + return vec; + } +} + +void OverlayImpl::send_message_to_neighbours(td::BufferSlice data) { + for (auto &n : peer_list_.neighbours_) { + td::actor::send_closure(manager_, &OverlayManager::send_message, n, local_id_, overlay_id_, data.clone()); + } +} + +size_t OverlayImpl::neighbours_cnt() const { + return peer_list_.neighbours_.size(); +} + +void OverlayImpl::update_root_member_list(std::vector ids, + std::vector root_public_keys, OverlayMemberCertificate cert) { + td::uint32 expectd_size = + (td::uint32)(ids.size() + root_public_keys.size() * opts_.max_slaves_in_semiprivate_overlay_); + if (expectd_size > opts_.max_peers_) { + opts_.max_peers_ = expectd_size; + } + if (expectd_size > opts_.max_neighbours_) { + opts_.max_neighbours_ = expectd_size; + } + std::sort(ids.begin(), ids.end()); + auto old_root_public_keys = std::move(peer_list_.root_public_keys_); + for (const auto &pub_key : root_public_keys) { + auto it = old_root_public_keys.find(pub_key); + if (it != old_root_public_keys.end()) { + peer_list_.root_public_keys_.emplace(it->first, std::move(it->second)); + } else { + peer_list_.root_public_keys_.emplace(pub_key, PeerList::SlaveKeys{}); + } + } + std::vector to_del; + peer_list_.peers_.iterate([&](const adnl::AdnlNodeIdShort &key, OverlayPeer &peer) { + peer.set_permanent(std::binary_search(ids.begin(), ids.end(), key)); + if (peer.is_permanent_member()) { + peer.clear_certificate(); + } else { + auto S = validate_peer_certificate(peer.get_id(), peer.certificate()); + if (S.is_error()) { + to_del.push_back(peer.get_id()); + } + } + }); + for (const auto &id : to_del) { + del_peer(id); + } + for (const auto &id : ids) { + if (!peer_list_.peers_.exists(id)) { + OverlayNode node(id, overlay_id_, opts_.default_permanent_members_flags_); + OverlayPeer peer(std::move(node)); + peer.set_permanent(true); + CHECK(peer.is_permanent_member()); + peer_list_.peers_.insert(std::move(id), std::move(peer)); + } + } + + update_member_certificate(std::move(cert)); + update_neighbours(0); +} + +void OverlayImpl::update_member_certificate(OverlayMemberCertificate cert) { + peer_list_.cert_ = std::move(cert); + + if (is_persistent_node(local_id_)) { + peer_list_.local_cert_is_valid_until_ = td::Timestamp::in(86400.0 * 365 * 100); /* 100 years */ + } else { + auto R = validate_peer_certificate(local_id_, &peer_list_.cert_); + if (R.is_ok()) { + peer_list_.local_cert_is_valid_until_ = td::Timestamp::at_unix(cert.expire_at()); + } else { + peer_list_.local_cert_is_valid_until_ = td::Timestamp::never(); + } + } +} + +bool OverlayImpl::has_valid_membership_certificate() { + if (overlay_type_ != OverlayType::CertificatedMembers) { + return true; + } + + if (!peer_list_.local_cert_is_valid_until_) { + return false; + } + + return !peer_list_.local_cert_is_valid_until_.is_in_past(); +} + } // namespace overlay } // namespace ton diff --git a/overlay/overlay.cpp b/overlay/overlay.cpp index 43b3b7e5a..93ae801c9 100644 --- a/overlay/overlay.cpp +++ b/overlay/overlay.cpp @@ -18,6 +18,7 @@ */ #include "auto/tl/ton_api.h" #include "td/utils/Random.h" +#include "common/delay.h" #include "adnl/utils.hpp" #include "dht/dht.h" @@ -26,41 +27,62 @@ #include "auto/tl/ton_api.hpp" #include "keys/encryptor.h" +#include "td/utils/Status.h" #include "td/utils/StringBuilder.h" +#include "td/utils/port/signals.h" +#include namespace ton { namespace overlay { -td::actor::ActorOwn Overlay::create(td::actor::ActorId keyring, - td::actor::ActorId adnl, - td::actor::ActorId manager, - td::actor::ActorId dht_node, adnl::AdnlNodeIdShort local_id, - OverlayIdFull overlay_id, std::unique_ptr callback, - OverlayPrivacyRules rules, td::string scope, OverlayOptions opts) { - auto R = td::actor::create_actor("overlay", keyring, adnl, manager, dht_node, local_id, - std::move(overlay_id), true, std::vector(), - std::move(callback), std::move(rules), scope, opts); - return td::actor::ActorOwn(std::move(R)); -} - -td::actor::ActorOwn Overlay::create(td::actor::ActorId keyring, - td::actor::ActorId adnl, - td::actor::ActorId manager, - td::actor::ActorId dht_node, adnl::AdnlNodeIdShort local_id, - OverlayIdFull overlay_id, std::vector nodes, - std::unique_ptr callback, OverlayPrivacyRules rules, - std::string scope) { - auto R = td::actor::create_actor("overlay", keyring, adnl, manager, dht_node, local_id, - std::move(overlay_id), false, std::move(nodes), std::move(callback), - std::move(rules), std::move(scope)); - return td::actor::ActorOwn(std::move(R)); +const OverlayMemberCertificate OverlayNode::empty_certificate_{}; + +static std::string overlay_actor_name(const OverlayIdFull &overlay_id) { + return PSTRING() << "overlay." << overlay_id.compute_short_id().bits256_value().to_hex().substr(0, 4); +} + +td::actor::ActorOwn Overlay::create_public(td::actor::ActorId keyring, + td::actor::ActorId adnl, + td::actor::ActorId manager, + td::actor::ActorId dht_node, + adnl::AdnlNodeIdShort local_id, OverlayIdFull overlay_id, + std::unique_ptr callback, + OverlayPrivacyRules rules, td::string scope, OverlayOptions opts) { + return td::actor::create_actor( + overlay_actor_name(overlay_id), keyring, adnl, manager, dht_node, local_id, std::move(overlay_id), + OverlayType::Public, std::vector(), std::vector(), + OverlayMemberCertificate{}, std::move(callback), std::move(rules), std::move(scope), std::move(opts)); +} + +td::actor::ActorOwn Overlay::create_private( + td::actor::ActorId keyring, td::actor::ActorId adnl, + td::actor::ActorId manager, td::actor::ActorId dht_node, adnl::AdnlNodeIdShort local_id, + OverlayIdFull overlay_id, std::vector nodes, std::unique_ptr callback, + OverlayPrivacyRules rules, std::string scope, OverlayOptions opts) { + return td::actor::create_actor( + overlay_actor_name(overlay_id), keyring, adnl, manager, dht_node, local_id, std::move(overlay_id), + OverlayType::FixedMemberList, std::move(nodes), std::vector(), OverlayMemberCertificate{}, + std::move(callback), std::move(rules), std::move(scope)); +} + +td::actor::ActorOwn Overlay::create_semiprivate( + td::actor::ActorId keyring, td::actor::ActorId adnl, + td::actor::ActorId manager, td::actor::ActorId dht_node, adnl::AdnlNodeIdShort local_id, + OverlayIdFull overlay_id, std::vector nodes, std::vector root_public_keys, + OverlayMemberCertificate cert, std::unique_ptr callback, OverlayPrivacyRules rules, + std::string scope, OverlayOptions opts) { + return td::actor::create_actor(overlay_actor_name(overlay_id), keyring, adnl, manager, dht_node, + local_id, std::move(overlay_id), OverlayType::CertificatedMembers, + std::move(nodes), std::move(root_public_keys), std::move(cert), + std::move(callback), std::move(rules), std::move(scope), std::move(opts)); } OverlayImpl::OverlayImpl(td::actor::ActorId keyring, td::actor::ActorId adnl, td::actor::ActorId manager, td::actor::ActorId dht_node, - adnl::AdnlNodeIdShort local_id, OverlayIdFull overlay_id, bool pub, - std::vector nodes, std::unique_ptr callback, + adnl::AdnlNodeIdShort local_id, OverlayIdFull overlay_id, OverlayType overlay_type, + std::vector nodes, std::vector root_public_keys, + OverlayMemberCertificate cert, std::unique_ptr callback, OverlayPrivacyRules rules, td::string scope, OverlayOptions opts) : keyring_(keyring) , adnl_(adnl) @@ -69,37 +91,28 @@ OverlayImpl::OverlayImpl(td::actor::ActorId keyring, td::actor , local_id_(local_id) , id_full_(std::move(overlay_id)) , callback_(std::move(callback)) - , public_(pub) + , overlay_type_(overlay_type) , rules_(std::move(rules)) , scope_(scope) , announce_self_(opts.announce_self_) - , frequent_dht_lookup_(opts.frequent_dht_lookup_) { + , opts_(std::move(opts)) { overlay_id_ = id_full_.compute_short_id(); + frequent_dht_lookup_ = opts_.frequent_dht_lookup_; + peer_list_.local_member_flags_ = opts_.local_overlay_member_flags_; - VLOG(OVERLAY_INFO) << this << ": creating " << (public_ ? "public" : "private"); + VLOG(OVERLAY_INFO) << this << ": creating"; - for (auto &node : nodes) { - CHECK(!public_); - auto X = OverlayNode{node, overlay_id_}; - do_add_peer(std::move(X)); - } - - update_neighbours(static_cast(nodes.size())); + auto nodes_size = static_cast(nodes.size()); + OverlayImpl::update_root_member_list(std::move(nodes), std::move(root_public_keys), std::move(cert)); + update_neighbours(nodes_size); } void OverlayImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::overlay_getRandomPeers &query, td::Promise promise) { - if (public_) { + if (overlay_type_ != OverlayType::FixedMemberList) { VLOG(OVERLAY_DEBUG) << this << ": received " << query.peers_->nodes_.size() << " nodes from " << src << " in getRandomPeers query"; - std::vector nodes; - for (auto &n : query.peers_->nodes_) { - auto N = OverlayNode::create(n); - if (N.is_ok()) { - nodes.emplace_back(N.move_as_ok()); - } - } - add_peers(std::move(nodes)); + add_peers(query.peers_); send_random_peers(src, std::move(promise)); } else { VLOG(OVERLAY_WARNING) << this << ": DROPPING getRandomPeers query from " << src << " in private overlay"; @@ -107,6 +120,19 @@ void OverlayImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::overlay_getR } } +void OverlayImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::overlay_getRandomPeersV2 &query, + td::Promise promise) { + if (overlay_type_ != OverlayType::FixedMemberList) { + VLOG(OVERLAY_DEBUG) << this << ": received " << query.peers_->nodes_.size() << " nodes from " << src + << " in getRandomPeers query"; + add_peers(query.peers_); + send_random_peers_v2(src, std::move(promise)); + } else { + VLOG(OVERLAY_WARNING) << this << ": DROPPING getRandomPeers query from " << src << " in private overlay"; + promise.set_error(td::Status::Error(ErrorCode::protoviolation, "overlay is private")); + } +} + void OverlayImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::overlay_getBroadcast &query, td::Promise promise) { auto it = broadcasts_.find(query.hash_); @@ -139,17 +165,14 @@ void OverlayImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::overlay_getB } */ -void OverlayImpl::receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice data, td::Promise promise) { - if (!public_) { - auto P = peers_.get(src); - if (P == nullptr) { - VLOG(OVERLAY_WARNING) << this << ": received query in private overlay from unknown source " << src; - promise.set_error(td::Status::Error(ErrorCode::protoviolation, "overlay is private")); - return; - } - } else { - on_ping_result(src, true); +void OverlayImpl::receive_query(adnl::AdnlNodeIdShort src, tl_object_ptr extra, + td::BufferSlice data, td::Promise promise) { + if (!is_valid_peer(src, extra ? extra->certificate_.get() : nullptr)) { + VLOG(OVERLAY_WARNING) << this << ": received query in private overlay from unknown source " << src; + promise.set_error(td::Status::Error(ErrorCode::protoviolation, "overlay is not public")); + return; } + auto R = fetch_tl_object(data.clone(), true); if (R.is_error()) { @@ -167,16 +190,25 @@ void OverlayImpl::receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice data, td::Status OverlayImpl::process_broadcast(adnl::AdnlNodeIdShort message_from, tl_object_ptr bcast) { + if (peer_list_.local_member_flags_ & OverlayMemberFlags::DoNotReceiveBroadcasts) { + return td::Status::OK(); + } return BroadcastSimple::create(this, message_from, std::move(bcast)); } td::Status OverlayImpl::process_broadcast(adnl::AdnlNodeIdShort message_from, tl_object_ptr b) { + if (peer_list_.local_member_flags_ & OverlayMemberFlags::DoNotReceiveBroadcasts) { + return td::Status::OK(); + } return OverlayFecBroadcastPart::create(this, message_from, std::move(b)); } td::Status OverlayImpl::process_broadcast(adnl::AdnlNodeIdShort message_from, tl_object_ptr b) { + if (peer_list_.local_member_flags_ & OverlayMemberFlags::DoNotReceiveBroadcasts) { + return td::Status::OK(); + } return OverlayFecBroadcastPart::create(this, message_from, std::move(b)); } @@ -188,6 +220,7 @@ td::Status OverlayImpl::process_broadcast(adnl::AdnlNodeIdShort message_from, td::Status OverlayImpl::process_broadcast(adnl::AdnlNodeIdShort message_from, tl_object_ptr msg) { + return td::Status::OK(); // disable this logic for now auto it = fec_broadcasts_.find(msg->hash_); if (it != fec_broadcasts_.end()) { VLOG(OVERLAY_DEBUG) << this << ": received fec opt-out message from " << message_from << " for broadcast " @@ -202,6 +235,7 @@ td::Status OverlayImpl::process_broadcast(adnl::AdnlNodeIdShort message_from, td::Status OverlayImpl::process_broadcast(adnl::AdnlNodeIdShort message_from, tl_object_ptr msg) { + return td::Status::OK(); // disable this logic for now auto it = fec_broadcasts_.find(msg->hash_); if (it != fec_broadcasts_.end()) { VLOG(OVERLAY_DEBUG) << this << ": received fec completed message from " << message_from << " for broadcast " @@ -221,15 +255,13 @@ td::Status OverlayImpl::process_broadcast(adnl::AdnlNodeIdShort message_from, return td::Status::OK(); } -void OverlayImpl::receive_message(adnl::AdnlNodeIdShort src, td::BufferSlice data) { - if (!public_) { - if (peers_.get(src) == nullptr) { - VLOG(OVERLAY_WARNING) << this << ": received query in private overlay from unknown source " << src; - return; - } - } else { - on_ping_result(src, true); +void OverlayImpl::receive_message(adnl::AdnlNodeIdShort src, tl_object_ptr extra, + td::BufferSlice data) { + if (!is_valid_peer(src, extra ? extra->certificate_.get() : nullptr)) { + VLOG(OVERLAY_WARNING) << this << ": received message in private overlay from unknown source " << src; + return; } + auto X = fetch_tl_object(data.clone(), true); if (X.is_error()) { VLOG(OVERLAY_DEBUG) << this << ": received custom message"; @@ -244,44 +276,52 @@ void OverlayImpl::receive_message(adnl::AdnlNodeIdShort src, td::BufferSlice dat void OverlayImpl::alarm() { bcast_gc(); - - if(update_throughput_at_.is_in_past()) { + + if (update_throughput_at_.is_in_past()) { double t_elapsed = td::Time::now() - last_throughput_update_.at(); auto SelfId = actor_id(this); - peers_.iterate([&](const adnl::AdnlNodeIdShort &key, OverlayPeer &peer) { - peer.throughput_out_bytes = static_cast(peer.throughput_out_bytes_ctr / t_elapsed); - peer.throughput_in_bytes = static_cast(peer.throughput_in_bytes_ctr / t_elapsed); - - peer.throughput_out_packets = static_cast(peer.throughput_out_packets_ctr / t_elapsed); - peer.throughput_in_packets = static_cast(peer.throughput_in_packets_ctr / t_elapsed); - - peer.throughput_out_bytes_ctr = 0; - peer.throughput_in_bytes_ctr = 0; - - peer.throughput_out_packets_ctr = 0; - peer.throughput_in_packets_ctr = 0; - + iterate_all_peers([&](const adnl::AdnlNodeIdShort &key, OverlayPeer &peer) { + peer.traffic = peer.traffic_ctr; + peer.traffic.normalize(t_elapsed); + peer.traffic_ctr = {}; + peer.traffic_responses = peer.traffic_responses_ctr; + peer.traffic_responses.normalize(t_elapsed); + peer.traffic_responses_ctr = {}; + auto P = td::PromiseCreator::lambda([SelfId, peer_id = key](td::Result result) { result.ensure(); td::actor::send_closure(SelfId, &Overlay::update_peer_ip_str, peer_id, result.move_as_ok()); }); - + td::actor::send_closure(adnl_, &adnl::AdnlSenderInterface::get_conn_ip_str, local_id_, key, std::move(P)); }); - + total_traffic = total_traffic_ctr; + total_traffic.normalize(t_elapsed); + total_traffic_ctr = {}; + total_traffic_responses = total_traffic_responses_ctr; + total_traffic_responses.normalize(t_elapsed); + total_traffic_responses_ctr = {}; + update_throughput_at_ = td::Timestamp::in(50.0); last_throughput_update_ = td::Timestamp::now(); } - - if (public_) { - if (peers_.size() > 0) { + + if (overlay_type_ != OverlayType::FixedMemberList) { + if (has_valid_membership_certificate()) { auto P = get_random_peer(); if (P) { - send_random_peers(P->get_id(), {}); + if (overlay_type_ == OverlayType::Public) { + send_random_peers(P->get_id(), {}); + } else { + send_random_peers_v2(P->get_id(), {}); + } } + } else { + VLOG(OVERLAY_WARNING) << "meber certificate ist invalid, valid_until=" + << peer_list_.local_cert_is_valid_until_.at_unix(); } - if (next_dht_query_ && next_dht_query_.is_in_past()) { + if (next_dht_query_ && next_dht_query_.is_in_past() && overlay_type_ == OverlayType::Public) { next_dht_query_ = td::Timestamp::never(); std::function callback = [SelfId = actor_id(this)](dht::DhtValue value) { td::actor::send_closure(SelfId, &OverlayImpl::receive_dht_nodes, std::move(value)); @@ -292,21 +332,22 @@ void OverlayImpl::alarm() { td::actor::send_closure(dht_node_, &dht::Dht::get_value_many, dht::DhtKey{overlay_id_.pubkey_hash(), "nodes", 0}, std::move(callback), std::move(on_finish)); } - if (update_db_at_.is_in_past()) { - if (peers_.size() > 0) { - std::vector vec; - for (td::uint32 i = 0; i < 20; i++) { - auto P = get_random_peer(); - if (!P) { - break; - } - vec.push_back(P->get()); + if (update_db_at_.is_in_past() && overlay_type_ == OverlayType::Public) { + std::vector vec; + for (td::uint32 i = 0; i < 20; i++) { + auto P = get_random_peer(); + if (!P) { + break; } + vec.push_back(P->get_node()->clone()); + } + if (vec.size() > 0) { td::actor::send_closure(manager_, &OverlayManager::save_to_db, local_id_, overlay_id_, std::move(vec)); } update_db_at_ = td::Timestamp::in(60.0); } + update_neighbours(0); alarm_timestamp() = td::Timestamp::in(1.0); } else { update_neighbours(0); @@ -315,7 +356,7 @@ void OverlayImpl::alarm() { } void OverlayImpl::receive_dht_nodes(dht::DhtValue v) { - CHECK(public_); + CHECK(overlay_type_ == OverlayType::Public); auto R = fetch_tl_object(v.value().clone(), true); if (R.is_ok()) { auto r = R.move_as_ok(); @@ -361,7 +402,7 @@ void OverlayImpl::dht_lookup_finished(td::Status S) { } void OverlayImpl::update_dht_nodes(OverlayNode node) { - if (!public_) { + if (overlay_type_ != OverlayType::Public) { return; } @@ -418,13 +459,16 @@ void OverlayImpl::bcast_gc() { CHECK(delivered_broadcasts_.size() == bcast_lru_.size()); } -void OverlayImpl::send_message_to_neighbours(td::BufferSlice data) { - for (auto &n : neighbours_) { - td::actor::send_closure(manager_, &OverlayManager::send_message, n, local_id_, overlay_id_, data.clone()); - } -} - void OverlayImpl::send_broadcast(PublicKeyHash send_as, td::uint32 flags, td::BufferSlice data) { + if (!has_valid_membership_certificate()) { + VLOG(OVERLAY_WARNING) << "member certificate is invalid, valid_until=" + << peer_list_.local_cert_is_valid_until_.at_unix(); + return; + } + if (!has_valid_broadcast_certificate(send_as, data.size(), false)) { + VLOG(OVERLAY_WARNING) << "broadcast source certificate is invalid"; + return; + } auto S = BroadcastSimple::create_new(actor_id(this), keyring_, send_as, std::move(data), flags); if (S.is_error()) { LOG(WARNING) << "failed to send broadcast: " << S; @@ -432,6 +476,15 @@ void OverlayImpl::send_broadcast(PublicKeyHash send_as, td::uint32 flags, td::Bu } void OverlayImpl::send_broadcast_fec(PublicKeyHash send_as, td::uint32 flags, td::BufferSlice data) { + if (!has_valid_membership_certificate()) { + VLOG(OVERLAY_WARNING) << "meber certificate is invalid, valid_until=" + << peer_list_.local_cert_is_valid_until_.at_unix(); + return; + } + if (!has_valid_broadcast_certificate(send_as, data.size(), true)) { + VLOG(OVERLAY_WARNING) << "broadcast source certificate is invalid"; + return; + } OverlayOutboundFecBroadcast::create(std::move(data), flags, actor_id(this), send_as); } @@ -450,6 +503,22 @@ td::Status OverlayImpl::check_date(td::uint32 date) { return td::Status::OK(); } +BroadcastCheckResult OverlayImpl::check_source_eligible(const PublicKeyHash &source, const Certificate *cert, + td::uint32 size, bool is_fec) { + if (size == 0) { + return BroadcastCheckResult::Forbidden; + } + + auto r = rules_.check_rules(source, size, is_fec); + if (!cert || r == BroadcastCheckResult::Allowed) { + return r; + } + + auto r2 = cert->check(source, overlay_id_, static_cast(td::Clocks::system()), size, is_fec); + r2 = broadcast_check_result_min(r2, rules_.check_rules(cert->issuer_hash(), size, is_fec)); + return broadcast_check_result_max(r, r2); +} + BroadcastCheckResult OverlayImpl::check_source_eligible(PublicKey source, const Certificate *cert, td::uint32 size, bool is_fec) { if (size == 0) { @@ -457,7 +526,7 @@ BroadcastCheckResult OverlayImpl::check_source_eligible(PublicKey source, const } auto short_id = source.compute_short_id(); - auto r = rules_.check_rules(source.compute_short_id(), size, is_fec); + auto r = rules_.check_rules(short_id, size, is_fec); if (!cert || r == BroadcastCheckResult::Allowed) { return r; } @@ -492,21 +561,23 @@ void OverlayImpl::register_fec_broadcast(std::unique_ptr bcast) { } void OverlayImpl::get_self_node(td::Promise promise) { - OverlayNode s{local_id_, overlay_id_}; + OverlayNode s{local_id_, overlay_id_, peer_list_.local_member_flags_}; auto to_sign = s.to_sign(); - auto P = td::PromiseCreator::lambda([oid = print_id(), s = std::move(s), promise = std::move(promise)]( - td::Result> R) mutable { - if (R.is_error()) { - auto S = R.move_as_error(); - LOG(ERROR) << oid << ": failed to get self node: " << S; - promise.set_error(std::move(S)); - return; - } - auto V = R.move_as_ok(); - s.update_signature(std::move(V.first)); - s.update_adnl_id(adnl::AdnlNodeIdFull{V.second}); - promise.set_value(std::move(s)); - }); + auto P = td::PromiseCreator::lambda( + [oid = print_id(), s = std::move(s), cert = peer_list_.cert_, + promise = std::move(promise)](td::Result> R) mutable { + if (R.is_error()) { + auto S = R.move_as_error(); + LOG(ERROR) << oid << ": failed to get self node: " << S; + promise.set_error(std::move(S)); + return; + } + auto V = R.move_as_ok(); + s.update_signature(std::move(V.first)); + s.update_adnl_id(adnl::AdnlNodeIdFull{V.second}); + s.update_certificate(std::move(cert)); + promise.set_value(std::move(s)); + }); td::actor::send_closure(keyring_, &keyring::Keyring::sign_add_get_public_key, local_id_.pubkey_hash(), std::move(to_sign), std::move(P)); @@ -598,17 +669,6 @@ void OverlayImpl::check_broadcast(PublicKeyHash src, td::BufferSlice data, td::P callback_->check_broadcast(src, overlay_id_, std::move(data), std::move(promise)); } -void OverlayImpl::update_peer_err_ctr(adnl::AdnlNodeIdShort peer_id, bool is_fec) { - auto src_peer = peers_.get(peer_id); - if(src_peer) { - if(is_fec) { - src_peer->fec_broadcast_errors++; - } else { - src_peer->broadcast_errors++; - } - } -} - void OverlayImpl::broadcast_checked(Overlay::BroadcastHash hash, td::Result R) { { auto it = broadcasts_.find(hash); @@ -630,30 +690,67 @@ void OverlayImpl::get_stats(td::Promiseoverlay_id_ = overlay_id_.bits256_value(); res->overlay_id_full_ = id_full_.pubkey().tl(); res->scope_ = scope_; - peers_.iterate([&](const adnl::AdnlNodeIdShort &key, const OverlayPeer &peer) { + iterate_all_peers([&](const adnl::AdnlNodeIdShort &key, const OverlayPeer &peer) { auto node_obj = create_tl_object(); node_obj->adnl_id_ = key.bits256_value(); - node_obj->t_out_bytes_ = peer.throughput_out_bytes; - node_obj->t_in_bytes_ = peer.throughput_in_bytes; - - node_obj->t_out_pckts_ = peer.throughput_out_packets; - node_obj->t_in_pckts_ = peer.throughput_in_packets; - + node_obj->traffic_ = peer.traffic.tl(); + node_obj->traffic_responses_ = peer.traffic_responses.tl(); node_obj->ip_addr_ = peer.ip_addr_str; - + node_obj->last_in_query_ = static_cast(peer.last_in_query_at.at_unix()); node_obj->last_out_query_ = static_cast(peer.last_out_query_at.at_unix()); - + node_obj->bdcst_errors_ = peer.broadcast_errors; node_obj->fec_bdcst_errors_ = peer.fec_broadcast_errors; - + + node_obj->is_neighbour_ = peer.is_neighbour(); + node_obj->is_alive_ = peer.is_alive(); + node_obj->node_flags_ = peer.get_node()->flags(); + res->nodes_.push_back(std::move(node_obj)); }); + res->total_traffic_ = total_traffic.tl(); + res->total_traffic_responses_ = total_traffic_responses.tl(); res->stats_.push_back( - create_tl_object("neighbours_cnt", PSTRING() << neighbours_.size())); + create_tl_object("neighbours_cnt", PSTRING() << neighbours_cnt())); + + callback_->get_stats_extra([promise = std::move(promise), res = std::move(res)](td::Result R) mutable { + if (R.is_ok()) { + res->extra_ = R.move_as_ok(); + } + promise.set_value(std::move(res)); + }); +} + +bool OverlayImpl::has_valid_broadcast_certificate(const PublicKeyHash &source, size_t size, bool is_fec) { + if (size > std::numeric_limits::max()) { + return false; + } + auto it = certs_.find(source); + return check_source_eligible(source, it == certs_.end() ? nullptr : it->second.get(), (td::uint32)size, is_fec) != + BroadcastCheckResult::Forbidden; +} + +void TrafficStats::add_packet(td::uint64 size, bool in) { + if (in) { + ++in_packets; + in_bytes += size; + } else { + ++out_packets; + out_bytes += size; + } +} + +void TrafficStats::normalize(double elapsed) { + out_bytes = static_cast(out_bytes / elapsed); + in_bytes = static_cast(in_bytes / elapsed); + out_packets = static_cast(out_packets / elapsed); + in_packets = static_cast(in_packets / elapsed); +} - promise.set_value(std::move(res)); +tl_object_ptr TrafficStats::tl() const { + return create_tl_object(out_bytes, in_bytes, out_packets, in_packets); } } // namespace overlay diff --git a/overlay/overlay.h b/overlay/overlay.h index c1fe9643c..ce9f40d3f 100644 --- a/overlay/overlay.h +++ b/overlay/overlay.h @@ -18,6 +18,7 @@ */ #pragma once +#include "auto/tl/ton_api.h" #include "td/utils/buffer.h" #include "td/utils/int_types.h" @@ -37,24 +38,29 @@ class Overlay : public td::actor::Actor { using BroadcastDataHash = td::Bits256; using BroadcastPartHash = td::Bits256; - static td::actor::ActorOwn create(td::actor::ActorId keyring, - td::actor::ActorId adnl, - td::actor::ActorId manager, - td::actor::ActorId dht_node, adnl::AdnlNodeIdShort local_id, - OverlayIdFull overlay_id, std::unique_ptr callback, - OverlayPrivacyRules rules, td::string scope, OverlayOptions opts = {}); - static td::actor::ActorOwn create(td::actor::ActorId keyring, - td::actor::ActorId adnl, - td::actor::ActorId manager, - td::actor::ActorId dht_node, adnl::AdnlNodeIdShort local_id, - OverlayIdFull overlay_id, std::vector nodes, - std::unique_ptr callback, OverlayPrivacyRules rules, - std::string scope); + static td::actor::ActorOwn create_public( + td::actor::ActorId keyring, td::actor::ActorId adnl, + td::actor::ActorId manager, td::actor::ActorId dht_node, adnl::AdnlNodeIdShort local_id, + OverlayIdFull overlay_id, std::unique_ptr callback, OverlayPrivacyRules rules, + td::string scope, OverlayOptions opts = {}); + static td::actor::ActorOwn create_private( + td::actor::ActorId keyring, td::actor::ActorId adnl, + td::actor::ActorId manager, td::actor::ActorId dht_node, adnl::AdnlNodeIdShort local_id, + OverlayIdFull overlay_id, std::vector nodes, std::unique_ptr callback, + OverlayPrivacyRules rules, std::string scope, OverlayOptions opts = {}); + static td::actor::ActorOwn create_semiprivate( + td::actor::ActorId keyring, td::actor::ActorId adnl, + td::actor::ActorId manager, td::actor::ActorId dht_node, adnl::AdnlNodeIdShort local_id, + OverlayIdFull overlay_id, std::vector nodes, std::vector root_public_keys, + OverlayMemberCertificate cert, std::unique_ptr callback, OverlayPrivacyRules rules, + std::string scope, OverlayOptions opts = {}); virtual void update_dht_node(td::actor::ActorId dht) = 0; - virtual void receive_message(adnl::AdnlNodeIdShort src, td::BufferSlice data) = 0; - virtual void receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice data, td::Promise promise) = 0; + virtual void receive_message(adnl::AdnlNodeIdShort src, tl_object_ptr extra, + td::BufferSlice data) = 0; + virtual void receive_query(adnl::AdnlNodeIdShort src, tl_object_ptr extra, + td::BufferSlice data, td::Promise promise) = 0; virtual void send_message_to_neighbours(td::BufferSlice data) = 0; virtual void send_broadcast(PublicKeyHash send_as, td::uint32 flags, td::BufferSlice data) = 0; virtual void send_broadcast_fec(PublicKeyHash send_as, td::uint32 flags, td::BufferSlice data) = 0; @@ -64,12 +70,19 @@ class Overlay : public td::actor::Actor { virtual void add_certificate(PublicKeyHash key, std::shared_ptr) = 0; virtual void set_privacy_rules(OverlayPrivacyRules rules) = 0; virtual void receive_nodes_from_db(tl_object_ptr nodes) = 0; + virtual void receive_nodes_from_db_v2(tl_object_ptr nodes) = 0; virtual void get_stats(td::Promise> promise) = 0; - virtual void update_throughput_out_ctr(adnl::AdnlNodeIdShort peer_id, td::uint32 msg_size, bool is_query) = 0; - virtual void update_throughput_in_ctr(adnl::AdnlNodeIdShort peer_id, td::uint32 msg_size, bool is_query) = 0; + virtual void update_throughput_out_ctr(adnl::AdnlNodeIdShort peer_id, td::uint64 msg_size, bool is_query, + bool is_response) = 0; + virtual void update_throughput_in_ctr(adnl::AdnlNodeIdShort peer_id, td::uint64 msg_size, bool is_query, + bool is_response) = 0; virtual void update_peer_ip_str(adnl::AdnlNodeIdShort peer_id, td::string ip_str) = 0; + virtual void update_member_certificate(OverlayMemberCertificate cert) = 0; + virtual void update_root_member_list(std::vector nodes, + std::vector root_public_keys, OverlayMemberCertificate cert) = 0; //virtual void receive_broadcast(td::BufferSlice data) = 0; //virtual void subscribe(std::unique_ptr callback) = 0; + virtual void forget_peer(adnl::AdnlNodeIdShort peer_id) = 0; }; } // namespace overlay diff --git a/overlay/overlay.hpp b/overlay/overlay.hpp index 8fb3d91d7..43822c5a1 100644 --- a/overlay/overlay.hpp +++ b/overlay/overlay.hpp @@ -18,11 +18,15 @@ */ #pragma once +#include +#include #include #include #include +#include #include +#include "adnl/adnl-node-id.hpp" #include "overlay.h" #include "overlay-manager.h" #include "overlay-fec.hpp" @@ -32,6 +36,9 @@ #include "td/utils/DecTree.h" #include "td/utils/List.h" +#include "td/utils/Status.h" +#include "td/utils/Time.h" +#include "td/utils/buffer.h" #include "td/utils/overloaded.h" #include "fec/fec.h" @@ -40,6 +47,8 @@ #include "auto/tl/ton_api.h" #include "auto/tl/ton_api.hpp" +#include "td/utils/port/signals.h" +#include "tl-utils/common-utils.hpp" namespace ton { @@ -50,6 +59,18 @@ namespace overlay { class OverlayImpl; +struct TrafficStats { + td::uint64 out_bytes = 0; + td::uint64 in_bytes = 0; + + td::uint32 out_packets = 0; + td::uint32 in_packets = 0; + + void add_packet(td::uint64 size, bool in); + void normalize(double elapsed); + tl_object_ptr tl() const; +}; + class OverlayPeer { public: adnl::AdnlNodeIdShort get_id() const { @@ -58,15 +79,17 @@ class OverlayPeer { adnl::AdnlNodeIdFull get_full_id() const { return node_.adnl_id_full(); } - OverlayNode get() const { - return node_.clone(); + const OverlayNode *get_node() const { + return &node_; } void update(OverlayNode node) { CHECK(get_id() == node.adnl_id_short()); - if (node.version() > node_.version()) { - node_ = std::move(node); - } + node_.update(std::move(node)); } + void update_certificate(OverlayMemberCertificate cert) { + node_.update_certificate(std::move(cert)); + } + OverlayPeer(OverlayNode node) : node_(std::move(node)) { id_ = node_.adnl_id_short(); } @@ -95,24 +118,35 @@ class OverlayPeer { return is_alive_; } - td::uint32 throughput_out_bytes = 0; - td::uint32 throughput_in_bytes = 0; - - td::uint32 throughput_out_packets = 0; - td::uint32 throughput_in_packets = 0; - - td::uint32 throughput_out_bytes_ctr = 0; - td::uint32 throughput_in_bytes_ctr = 0; - - td::uint32 throughput_out_packets_ctr = 0; - td::uint32 throughput_in_packets_ctr = 0; - + bool is_permanent_member() const { + return is_permanent_member_; + } + + void set_permanent(bool value) { + is_permanent_member_ = value; + } + + void clear_certificate() { + node_.clear_certificate(); + } + + auto certificate() const { + return node_.certificate(); + } + + bool has_full_id() const { + return node_.has_full_id(); + } + + TrafficStats traffic, traffic_ctr; + TrafficStats traffic_responses, traffic_responses_ctr; + td::uint32 broadcast_errors = 0; td::uint32 fec_broadcast_errors = 0; - + td::Timestamp last_in_query_at = td::Timestamp::now(); td::Timestamp last_out_query_at = td::Timestamp::now(); - + td::string ip_addr_str = "undefined"; private: @@ -122,6 +156,7 @@ class OverlayPeer { bool is_neighbour_ = false; size_t missed_pings_ = 0; bool is_alive_ = true; + bool is_permanent_member_ = false; td::Timestamp last_ping_at_ = td::Timestamp::now(); }; @@ -129,19 +164,23 @@ class OverlayImpl : public Overlay { public: OverlayImpl(td::actor::ActorId keyring, td::actor::ActorId adnl, td::actor::ActorId manager, td::actor::ActorId dht_node, - adnl::AdnlNodeIdShort local_id, OverlayIdFull overlay_id, bool pub, - std::vector nodes, std::unique_ptr callback, - OverlayPrivacyRules rules, td::string scope = "{ \"type\": \"undefined\" }", OverlayOptions opts = {}); + adnl::AdnlNodeIdShort local_id, OverlayIdFull overlay_id, OverlayType overlay_type, + std::vector nodes, std::vector root_public_keys, + OverlayMemberCertificate cert, std::unique_ptr callback, OverlayPrivacyRules rules, + td::string scope = "{ \"type\": \"undefined\" }", OverlayOptions opts = {}); void update_dht_node(td::actor::ActorId dht) override { dht_node_ = dht; } - void receive_message(adnl::AdnlNodeIdShort src, td::BufferSlice data) override; - void receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice data, td::Promise promise) override; + void receive_message(adnl::AdnlNodeIdShort src, tl_object_ptr extra, + td::BufferSlice data) override; + void receive_query(adnl::AdnlNodeIdShort src, tl_object_ptr extra, + td::BufferSlice data, td::Promise promise) override; void send_message_to_neighbours(td::BufferSlice data) override; void send_broadcast(PublicKeyHash send_as, td::uint32 flags, td::BufferSlice data) override; void send_broadcast_fec(PublicKeyHash send_as, td::uint32 flags, td::BufferSlice data) override; void receive_nodes_from_db(tl_object_ptr nodes) override; + void receive_nodes_from_db_v2(tl_object_ptr nodes) override; void get_self_node(td::Promise promise); @@ -149,8 +188,8 @@ class OverlayImpl : public Overlay { void start_up() override { update_throughput_at_ = td::Timestamp::in(50.0); last_throughput_update_ = td::Timestamp::now(); - - if (public_) { + + if (overlay_type_ == OverlayType::Public) { update_db_at_ = td::Timestamp::in(60.0); } alarm_timestamp() = td::Timestamp::in(1); @@ -158,13 +197,17 @@ class OverlayImpl : public Overlay { void on_ping_result(adnl::AdnlNodeIdShort peer, bool success); void receive_random_peers(adnl::AdnlNodeIdShort src, td::Result R); + void receive_random_peers_v2(adnl::AdnlNodeIdShort src, td::Result R); void send_random_peers(adnl::AdnlNodeIdShort dst, td::Promise promise); + void send_random_peers_v2(adnl::AdnlNodeIdShort dst, td::Promise promise); void send_random_peers_cont(adnl::AdnlNodeIdShort dst, OverlayNode node, td::Promise promise); + void send_random_peers_v2_cont(adnl::AdnlNodeIdShort dst, OverlayNode node, td::Promise promise); void get_overlay_random_peers(td::uint32 max_peers, td::Promise> promise) override; void set_privacy_rules(OverlayPrivacyRules rules) override; void add_certificate(PublicKeyHash key, std::shared_ptr cert) override { certs_[key] = std::move(cert); } + void update_member_certificate(OverlayMemberCertificate cert) override; void receive_dht_nodes(dht::DhtValue v); void dht_lookup_finished(td::Status S); @@ -188,6 +231,8 @@ class OverlayImpl : public Overlay { td::Status check_date(td::uint32 date); BroadcastCheckResult check_source_eligible(PublicKey source, const Certificate *cert, td::uint32 size, bool is_fec); + BroadcastCheckResult check_source_eligible(const PublicKeyHash &source, const Certificate *cert, td::uint32 size, + bool is_fec); td::Status check_delivered(BroadcastHash hash); void broadcast_checked(Overlay::BroadcastHash hash, td::Result R); @@ -206,17 +251,7 @@ class OverlayImpl : public Overlay { void send_new_fec_broadcast_part(PublicKeyHash local_id, Overlay::BroadcastDataHash data_hash, td::uint32 size, td::uint32 flags, td::BufferSlice part, td::uint32 seqno, fec::FecType fec_type, td::uint32 date); - std::vector get_neighbours(td::uint32 max_size = 0) const { - if (max_size == 0 || max_size >= neighbours_.size()) { - return neighbours_; - } else { - std::vector vec; - for (td::uint32 i = 0; i < max_size; i++) { - vec.push_back(neighbours_[td::Random::fast(0, static_cast(neighbours_.size()) - 1)]); - } - return vec; - } - } + std::vector get_neighbours(td::uint32 max_size = 0) const; td::actor::ActorId overlay_manager() const { return manager_; } @@ -236,38 +271,58 @@ class OverlayImpl : public Overlay { td::Result get_encryptor(PublicKey source); void get_stats(td::Promise> promise) override; - - void update_throughput_out_ctr(adnl::AdnlNodeIdShort peer_id, td::uint32 msg_size, bool is_query) override { - auto out_peer = peers_.get(peer_id); - if(out_peer) { - out_peer->throughput_out_bytes_ctr += msg_size; - out_peer->throughput_out_packets_ctr++; - - if(is_query) - { - out_peer->last_out_query_at = td::Timestamp::now(); - } - } + + void update_throughput_out_ctr(adnl::AdnlNodeIdShort peer_id, td::uint64 msg_size, bool is_query, + bool is_response) override; + + void update_throughput_in_ctr(adnl::AdnlNodeIdShort peer_id, td::uint64 msg_size, bool is_query, + bool is_response) override; + + void update_peer_ip_str(adnl::AdnlNodeIdShort peer_id, td::string ip_str) override; + + void update_root_member_list(std::vector nodes, std::vector root_public_keys, + OverlayMemberCertificate cert) override; + + bool is_valid_peer(const adnl::AdnlNodeIdShort &id, const ton_api::overlay_MemberCertificate *certificate); + bool is_persistent_node(const adnl::AdnlNodeIdShort &id); + + td::uint32 max_data_bcasts() const { + return 100; } - - void update_throughput_in_ctr(adnl::AdnlNodeIdShort peer_id, td::uint32 msg_size, bool is_query) override { - auto in_peer = peers_.get(peer_id); - if(in_peer) { - in_peer->throughput_in_bytes_ctr += msg_size; - in_peer->throughput_in_packets_ctr++; - - if(is_query) - { - in_peer->last_in_query_at = td::Timestamp::now(); - } - } + td::uint32 max_bcasts() const { + return 1000; } - - void update_peer_ip_str(adnl::AdnlNodeIdShort peer_id, td::string ip_str) override { - auto fpeer = peers_.get(peer_id); - if(fpeer) { - fpeer->ip_addr_str = ip_str; - } + td::uint32 max_fec_bcasts() const { + return 20; + } + td::uint32 max_sources() const { + return 10; + } + td::uint32 max_encryptors() const { + return 16; + } + + td::uint32 max_neighbours() const { + return opts_.max_neighbours_; + } + + td::uint32 max_peers() const { + return opts_.max_peers_; + } + + td::uint32 nodes_to_send() const { + return opts_.nodes_to_send_; + } + + td::uint32 propagate_broadcast_to() const { + return opts_.propagate_broadcast_to_; + } + + bool has_valid_membership_certificate(); + bool has_valid_broadcast_certificate(const PublicKeyHash &source, size_t size, bool is_fec); + + void forget_peer(adnl::AdnlNodeIdShort peer_id) override { + del_peer(peer_id); } private: @@ -278,6 +333,8 @@ class OverlayImpl : public Overlay { void process_query(adnl::AdnlNodeIdShort src, ton_api::overlay_getRandomPeers &query, td::Promise promise); + void process_query(adnl::AdnlNodeIdShort src, ton_api::overlay_getRandomPeersV2 &query, + td::Promise promise); void process_query(adnl::AdnlNodeIdShort src, ton_api::overlay_getBroadcast &query, td::Promise promise); void process_query(adnl::AdnlNodeIdShort src, ton_api::overlay_getBroadcastList &query, @@ -294,20 +351,28 @@ class OverlayImpl : public Overlay { td::Status process_broadcast(adnl::AdnlNodeIdShort message_from, tl_object_ptr msg); td::Status process_broadcast(adnl::AdnlNodeIdShort message_from, tl_object_ptr msg); - void do_add_peer(OverlayNode node); - void add_peer_in_cont(OverlayNode node); - void add_peer_in(OverlayNode node); + td::Status validate_peer_certificate(const adnl::AdnlNodeIdShort &node, const OverlayMemberCertificate &cert); + td::Status validate_peer_certificate(const adnl::AdnlNodeIdShort &node, const OverlayMemberCertificate *cert); + td::Status validate_peer_certificate(const adnl::AdnlNodeIdShort &node, ton_api::overlay_MemberCertificate *cert); void add_peer(OverlayNode node); void add_peers(std::vector nodes); + void add_peers(const tl_object_ptr &nodes); + void add_peers(const tl_object_ptr &nodes); void del_some_peers(); - void del_peer(adnl::AdnlNodeIdShort id); + void del_peer(const adnl::AdnlNodeIdShort &id); + void del_from_neighbour_list(OverlayPeer *P); + void del_from_neighbour_list(const adnl::AdnlNodeIdShort &id); + void iterate_all_peers(std::function cb); OverlayPeer *get_random_peer(bool only_alive = false); + bool is_root_public_key(const PublicKeyHash &key) const; + bool has_good_peers() const; + size_t neighbours_cnt() const; void finish_dht_query() { if (!next_dht_store_query_) { next_dht_store_query_ = td::Timestamp::in(td::Random::fast(60.0, 100.0)); } - if (frequent_dht_lookup_ && peers_.size() == bad_peers_.size()) { + if (frequent_dht_lookup_ && !has_good_peers()) { next_dht_query_ = td::Timestamp::in(td::Random::fast(6.0, 10.0)); } else { next_dht_query_ = next_dht_store_query_; @@ -322,14 +387,11 @@ class OverlayImpl : public Overlay { OverlayIdFull id_full_; OverlayIdShort overlay_id_; - td::DecTree peers_; td::Timestamp next_dht_query_ = td::Timestamp::in(1.0); td::Timestamp next_dht_store_query_ = td::Timestamp::in(1.0); td::Timestamp update_db_at_; td::Timestamp update_throughput_at_; td::Timestamp last_throughput_update_; - std::set bad_peers_; - adnl::AdnlNodeIdShort next_bad_peer_ = adnl::AdnlNodeIdShort::zero(); std::unique_ptr callback_; @@ -337,7 +399,6 @@ class OverlayImpl : public Overlay { std::map> fec_broadcasts_; std::set delivered_broadcasts_; - std::vector neighbours_; td::ListNode bcast_data_lru_; td::ListNode bcast_fec_lru_; std::queue bcast_lru_; @@ -346,33 +407,6 @@ class OverlayImpl : public Overlay { void bcast_gc(); - static td::uint32 max_data_bcasts() { - return 100; - } - static td::uint32 max_bcasts() { - return 1000; - } - static td::uint32 max_fec_bcasts() { - return 20; - } - static td::uint32 max_sources() { - return 10; - } - static td::uint32 max_neighbours() { - return 5; - } - static td::uint32 max_encryptors() { - return 16; - } - - static td::uint32 max_peers() { - return 20; - } - - static td::uint32 nodes_to_send() { - return 4; - } - static BroadcastHash get_broadcast_hash(adnl::AdnlNodeIdShort &src, td::Bits256 &data_hash) { td::uint8 buf[64]; td::MutableSlice m{buf, 64}; @@ -382,8 +416,7 @@ class OverlayImpl : public Overlay { return td::sha256_bits256(td::Slice(buf, 64)); } - bool public_; - bool semi_public_ = false; + OverlayType overlay_type_; OverlayPrivacyRules rules_; td::string scope_; bool announce_self_ = true; @@ -412,6 +445,27 @@ class OverlayImpl : public Overlay { td::ListNode encryptor_lru_; std::map> encryptor_map_; + + struct PeerList { + struct SlaveKey { + td::int32 expire_at{0}; + adnl::AdnlNodeIdShort node{}; + }; + using SlaveKeys = std::vector; + std::map root_public_keys_; + OverlayMemberCertificate cert_; + std::set bad_peers_; + adnl::AdnlNodeIdShort next_bad_peer_ = adnl::AdnlNodeIdShort::zero(); + td::DecTree peers_; + std::vector neighbours_; + + td::Timestamp local_cert_is_valid_until_; + td::uint32 local_member_flags_{0}; + } peer_list_; + TrafficStats total_traffic, total_traffic_ctr; + TrafficStats total_traffic_responses, total_traffic_responses_ctr; + + OverlayOptions opts_; }; } // namespace overlay diff --git a/overlay/overlays.h b/overlay/overlays.h index 6bf5852f2..cc112bc43 100644 --- a/overlay/overlays.h +++ b/overlay/overlays.h @@ -18,7 +18,9 @@ */ #pragma once +#include "adnl/adnl-node-id.hpp" #include "adnl/adnl.h" +#include "auto/tl/ton_api.h" #include "dht/dht.h" #include "td/actor/PromiseFuture.h" @@ -33,6 +35,8 @@ namespace ton { namespace overlay { +enum class OverlayType { Public, FixedMemberList, CertificatedMembers }; + class OverlayIdShort { public: OverlayIdShort() { @@ -88,6 +92,10 @@ struct CertificateFlags { enum Values : td::uint32 { AllowFec = 1, Trusted = 2 }; }; +struct OverlayMemberFlags { + enum Values : td::uint32 { DoNotReceiveBroadcasts = 1 }; +}; + enum BroadcastCheckResult { Forbidden = 1, NeedCheck = 2, Allowed = 3 }; inline BroadcastCheckResult broadcast_check_result_max(BroadcastCheckResult l, BroadcastCheckResult r) { @@ -108,7 +116,6 @@ class OverlayPrivacyRules { } BroadcastCheckResult check_rules(PublicKeyHash hash, td::uint32 size, bool is_fec) { - auto it = authorized_keys_.find(hash); if (it == authorized_keys_.end()) { if (size > max_unath_size_) { @@ -158,9 +165,110 @@ class Certificate { td::SharedSlice signature_; }; +class OverlayMemberCertificate { + public: + OverlayMemberCertificate() { + expire_at_ = std::numeric_limits::max(); + } + OverlayMemberCertificate(PublicKey signed_by, td::uint32 flags, td::int32 slot, td::int32 expire_at, + td::BufferSlice signature) + : signed_by_(std::move(signed_by)) + , flags_(flags) + , slot_(slot) + , expire_at_(expire_at) + , signature_(std::move(signature)) { + } + OverlayMemberCertificate(const OverlayMemberCertificate &other) + : signed_by_(other.signed_by_) + , flags_(other.flags_) + , slot_(other.slot_) + , expire_at_(other.expire_at_) + , signature_(other.signature_.clone()) { + } + OverlayMemberCertificate(OverlayMemberCertificate &&) = default; + OverlayMemberCertificate &operator=(OverlayMemberCertificate &&) = default; + OverlayMemberCertificate &operator=(const OverlayMemberCertificate &other) { + signed_by_ = other.signed_by_; + flags_ = other.flags_; + slot_ = other.slot_; + expire_at_ = other.expire_at_; + signature_ = other.signature_.clone(); + return *this; + } + explicit OverlayMemberCertificate(const ton_api::overlay_MemberCertificate *cert); + td::Status check_signature(const adnl::AdnlNodeIdShort &node); + + bool is_expired() const { + return expire_at_ < td::Clocks::system() - 3; + } + + bool is_expired(double cur_time) const { + return expire_at_ < cur_time - 3; + } + + tl_object_ptr tl() const { + if (empty()) { + return create_tl_object(); + } + return create_tl_object(signed_by_.tl(), flags_, slot_, expire_at_, + signature_.clone_as_buffer_slice()); + } + + const auto &issued_by() const { + return signed_by_; + } + + td::Slice signature() const { + return signature_.as_slice(); + } + + td::BufferSlice to_sign_data(const adnl::AdnlNodeIdShort &node) const { + return ton::create_serialize_tl_object(node.tl(), flags_, slot_, + expire_at_); + } + + bool empty() const { + return signed_by_.empty(); + } + + bool is_newer(const OverlayMemberCertificate &other) const { + return !empty() && expire_at_ > other.expire_at_; + } + + auto slot() const { + return slot_; + } + + auto expire_at() const { + return expire_at_; + } + + void set_signature(td::Slice signature) { + signature_ = td::SharedSlice(signature); + } + void set_signature(td::SharedSlice signature) { + signature_ = std::move(signature); + } + + private: + PublicKey signed_by_; + td::uint32 flags_; + td::int32 slot_; + td::int32 expire_at_ = std::numeric_limits::max(); + td::SharedSlice signature_; +}; + + struct OverlayOptions { bool announce_self_ = true; bool frequent_dht_lookup_ = false; + td::uint32 local_overlay_member_flags_ = 0; + td::int32 max_slaves_in_semiprivate_overlay_ = 5; + td::uint32 max_peers_ = 20; + td::uint32 max_neighbours_ = 5; + td::uint32 nodes_to_send_ = 4; + td::uint32 propagate_broadcast_to_ = 5; + td::uint32 default_permanent_members_flags_ = 0; }; class Overlays : public td::actor::Actor { @@ -175,6 +283,9 @@ class Overlays : public td::actor::Actor { td::Promise promise) { promise.set_value(td::Unit()); } + virtual void get_stats_extra(td::Promise promise) { + promise.set_result(""); + } virtual ~Callback() = default; }; @@ -192,6 +303,10 @@ class Overlays : public td::actor::Actor { return 1; } + static constexpr td::uint32 overlay_peer_ttl() { + return 600; + } + static td::actor::ActorOwn create(std::string db_root, td::actor::ActorId keyring, td::actor::ActorId adnl, td::actor::ActorId dht); @@ -201,11 +316,20 @@ class Overlays : public td::actor::Actor { std::unique_ptr callback, OverlayPrivacyRules rules, td::string scope) = 0; virtual void create_public_overlay_ex(adnl::AdnlNodeIdShort local_id, OverlayIdFull overlay_id, - std::unique_ptr callback, OverlayPrivacyRules rules, - td::string scope, OverlayOptions opts) = 0; + std::unique_ptr callback, OverlayPrivacyRules rules, td::string scope, + OverlayOptions opts) = 0; + virtual void create_semiprivate_overlay(adnl::AdnlNodeIdShort local_id, OverlayIdFull overlay_id, + std::vector nodes, + std::vector root_public_keys, + OverlayMemberCertificate certificate, + std::unique_ptr callback, OverlayPrivacyRules rules, + td::string scope, OverlayOptions opts) = 0; virtual void create_private_overlay(adnl::AdnlNodeIdShort local_id, OverlayIdFull overlay_id, std::vector nodes, std::unique_ptr callback, OverlayPrivacyRules rules, std::string scope) = 0; + virtual void create_private_overlay_ex(adnl::AdnlNodeIdShort local_id, OverlayIdFull overlay_id, + std::vector nodes, std::unique_ptr callback, + OverlayPrivacyRules rules, std::string scope, OverlayOptions opts) = 0; virtual void delete_overlay(adnl::AdnlNodeIdShort local_id, OverlayIdShort overlay_id) = 0; virtual void send_query(adnl::AdnlNodeIdShort dst, adnl::AdnlNodeIdShort src, OverlayIdShort overlay_id, @@ -239,9 +363,18 @@ class Overlays : public td::actor::Actor { virtual void update_certificate(adnl::AdnlNodeIdShort local_id, OverlayIdShort overlay_id, PublicKeyHash key, std::shared_ptr cert) = 0; + virtual void update_member_certificate(adnl::AdnlNodeIdShort local_id, OverlayIdShort overlay_id, + OverlayMemberCertificate certificate) = 0; + virtual void update_root_member_list(adnl::AdnlNodeIdShort local_id, OverlayIdShort overlay_id, + std::vector nodes, + std::vector root_public_keys, + OverlayMemberCertificate certificate) = 0; + virtual void get_overlay_random_peers(adnl::AdnlNodeIdShort local_id, OverlayIdShort overlay, td::uint32 max_peers, td::Promise> promise) = 0; virtual void get_stats(td::Promise> promise) = 0; + + virtual void forget_peer(adnl::AdnlNodeIdShort local_id, OverlayIdShort overlay, adnl::AdnlNodeIdShort peer_id) = 0; }; } // namespace overlay diff --git a/recent_changelog.md b/recent_changelog.md index 930fa9c9f..fb7433e5e 100644 --- a/recent_changelog.md +++ b/recent_changelog.md @@ -1,16 +1,17 @@ -## 2024.08 Update +## 2024.10 Update -1. Introduction of dispatch queues, message envelopes with transaction chain metadata, and explicitly stored msg_queue size, which will be activated by `Config8.version >= 8` and new `Config8.capabilities` bits: `capStoreOutMsgQueueSize`, `capMsgMetadata`, `capDeferMessages`. -2. A number of changes to transcation executor which will activated for `Config8.version >= 8`: - - Check mode on invalid `action_send_msg`. Ignore action if `IGNORE_ERROR` (+2) bit is set, bounce if `BOUNCE_ON_FAIL` (+16) bit is set. - - Slightly change random seed generation to fix mix of `addr_rewrite` and `addr`. - - Fill in `skipped_actions` for both invalid and valid messages with `IGNORE_ERROR` mode that can't be sent. - - Allow unfreeze through external messages. - - Don't use user-provided `fwd_fee` and `ihr_fee` for internal messages. -3. A few issues with broadcasts were fixed: stop on receiving last piece, response to AdnlMessageCreateChannel -4. A number of fixes and improvements for emulator and tonlib: correct work with config_addr, not accepted externals, bounces, debug ops gas consumption, added version and c5 dump, fixed tonlib crashes -5. Added new flags and commands to the node, in particular `--fast-state-serializer`, `getcollatoroptionsjson`, `setcollatoroptionsjson` +1. Parallel write to celldb: substantial improvement of sync and GC speed, especially with slow disks. +2. Decreased network traffic: only first block candidate is sent optimistically. +3. Improved channel creation and dht lookups, introduction of semi-private overlays +4. New LS dispatch queue related methods and improvement security +5. Fixing recursion in TVM continuations +6. Improved stats for actors, validator sessions, perf counters, overlays, adnl, rocksdb +7. Migration to C++20 +8. Improved block size estimates: account for depth in various structures +9. Fix bug with `<<` optimization in FunC +10. Minor changes of TVM which will be activated by `Config8.version >= 9` +11. Multiple minor improvements -Besides the work of the core team, this update is based on the efforts of @krigga (emulator), stonfi team, in particular @dbaranovstonfi and @hey-researcher (emulator), and @loeul, @xiaoxianBoy, @simlecode (typos in comments and docs). +Besides the work of the core team, this update is based on the efforts of @krigga (emulator), Arayz @ TonBit (LS security, TVM recursion), @ret2happy (UB in BLST). diff --git a/rldp2/RldpConnection.cpp b/rldp2/RldpConnection.cpp index c6f967282..47506178e 100644 --- a/rldp2/RldpConnection.cpp +++ b/rldp2/RldpConnection.cpp @@ -170,6 +170,9 @@ td::Timestamp RldpConnection::run(ConnectionCallback &callback) { if (in_flight_count_ > congestion_window_) { bdw_stats_.on_pause(now); } + if (in_flight_count_ == 0) { + bdw_stats_.on_pause(now); + } for (auto &inbound : inbound_transfers_) { alarm_timestamp.relax(run(inbound.first, inbound.second)); diff --git a/storage/CMakeLists.txt b/storage/CMakeLists.txt index 0208a33d7..30403f5e2 100644 --- a/storage/CMakeLists.txt +++ b/storage/CMakeLists.txt @@ -41,13 +41,11 @@ set(STORAGE_CLI_SOURCE add_library(storage STATIC ${STORAGE_SOURCE}) target_link_libraries(storage tdutils tdactor tddb ton_crypto tl_api ${JEMALLOC_LIBRARIES}) -target_include_directories(storage PUBLIC - $ +target_include_directories(storage PUBLIC $ ) add_executable(storage-cli ${STORAGE_CLI_SOURCE}) -target_link_libraries(storage-cli storage overlay tdutils tdactor adnl tl_api dht - rldp rldp2 fift-lib memprof terminal git ${JEMALLOC_LIBRARIES}) +target_link_libraries(storage-cli storage overlay tdutils tdactor adnl tl_api dht rldp rldp2 fift-lib memprof terminal git ${JEMALLOC_LIBRARIES}) set(STORAGE_TEST_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/test/storage.cpp diff --git a/storage/storage-daemon/CMakeLists.txt b/storage/storage-daemon/CMakeLists.txt index c987858f6..cabc61439 100644 --- a/storage/storage-daemon/CMakeLists.txt +++ b/storage/storage-daemon/CMakeLists.txt @@ -25,8 +25,7 @@ set(STORAGE_DAEMON_CLI_SOURCE ) add_executable(storage-daemon ${STORAGE_DAEMON_SOURCE}) -target_link_libraries(storage-daemon storage overlay tdutils tdactor adnl tl_api dht - rldp rldp2 fift-lib memprof git tonlib) +target_link_libraries(storage-daemon storage overlay tdutils tdactor adnl tl_api dht rldp rldp2 fift-lib memprof git tonlib) add_executable(storage-daemon-cli ${STORAGE_DAEMON_CLI_SOURCE}) -target_link_libraries(storage-daemon-cli tdutils tdactor adnllite tl_api tl_lite_api tl-lite-utils ton_block terminal git) \ No newline at end of file +target_link_libraries(storage-daemon-cli tdutils tdactor adnllite tl_api tl_lite_api tl-lite-utils ton_block terminal git) diff --git a/tdactor/CMakeLists.txt b/tdactor/CMakeLists.txt index 46dd03356..98b900a1e 100644 --- a/tdactor/CMakeLists.txt +++ b/tdactor/CMakeLists.txt @@ -3,16 +3,19 @@ cmake_minimum_required(VERSION 3.5 FATAL_ERROR) #SOURCE SETS set(TDACTOR_SOURCE td/actor/core/ActorExecutor.cpp + td/actor/core/ActorTypeStat.cpp td/actor/core/CpuWorker.cpp td/actor/core/IoWorker.cpp td/actor/core/Scheduler.cpp + td/actor/ActorStats.cpp td/actor/MultiPromise.cpp td/actor/actor.h td/actor/ActorId.h td/actor/ActorOwn.h td/actor/ActorShared.h + td/actor/ActorStats.h td/actor/common.h td/actor/PromiseFuture.h td/actor/MultiPromise.h @@ -27,6 +30,7 @@ set(TDACTOR_SOURCE td/actor/core/ActorMessage.h td/actor/core/ActorSignals.h td/actor/core/ActorState.h + td/actor/core/ActorTypeStat.h td/actor/core/CpuWorker.h td/actor/core/Context.h td/actor/core/IoWorker.h diff --git a/tdactor/td/actor/ActorStats.cpp b/tdactor/td/actor/ActorStats.cpp new file mode 100644 index 000000000..ad037204b --- /dev/null +++ b/tdactor/td/actor/ActorStats.cpp @@ -0,0 +1,245 @@ +#include "ActorStats.h" + +#include "td/utils/ThreadSafeCounter.h" +namespace td { +namespace actor { +void td::actor::ActorStats::start_up() { + auto now = td::Time::now(); + for (std::size_t i = 0; i < SIZE; i++) { + stat_[i] = td::TimedStat>(DURATIONS[i], now); + stat_[i].add_event(ActorTypeStats(), now); + } + begin_ts_ = td::Timestamp::now(); + begin_ticks_ = Clocks::rdtsc(); + loop(); +} +double ActorStats::estimate_inv_ticks_per_second() { + auto now = td::Timestamp::now(); + auto elapsed_seconds = now.at() - begin_ts_.at(); + auto now_ticks = td::Clocks::rdtsc(); + auto elapsed_ticks = now_ticks - begin_ticks_; + auto estimated_inv_ticks_per_second = + elapsed_seconds > 0.1 ? elapsed_seconds / double(elapsed_ticks) : Clocks::inv_ticks_per_second(); + return estimated_inv_ticks_per_second; +} + +std::string ActorStats::prepare_stats() { + auto estimated_inv_ticks_per_second = estimate_inv_ticks_per_second(); + + auto current_stats = td::actor::ActorTypeStatManager::get_stats(estimated_inv_ticks_per_second); + auto now = td::Timestamp::now(); + auto now_ticks = Clocks::rdtsc(); + + update(now); + + // Lets look at recent stats first + auto load_stats = [&](auto &timed_stat) { + auto res = current_stats; + auto &since = timed_stat.get_stat(now.at()); + auto duration = since.get_duration(estimated_inv_ticks_per_second); + if (since.first_) { + res -= since.first_.value(); + } + res /= duration; + return res.stats; + }; + auto stats_10s = load_stats(stat_[0]); + auto stats_10m = load_stats(stat_[1]); + current_stats /= double(now_ticks - begin_ticks_) * estimated_inv_ticks_per_second; + auto stats_forever = current_stats.stats; + + std::map current_perf_map; + std::map perf_map_10s; + std::map perf_map_10m; + std::map perf_values; + td::NamedPerfCounter::get_default().for_each( + [&](td::Slice name, td::int64 value_int64) { perf_values[name.str()] = double(value_int64); }); + for (auto &value_it : perf_values) { + const auto &name = value_it.first; + auto value = value_it.second; + + auto &perf_stat = pef_stats_[name]; + auto load_perf_stats = [&](auto &timed_stat, auto &m) { + double res = double(value); + auto &since = timed_stat.get_stat(now.at()); + auto duration = since.get_duration(estimated_inv_ticks_per_second); + if (since.first_) { + res -= since.first_.value(); + } + if (td::ends_with(name, ".duration")) { + res *= estimated_inv_ticks_per_second; + } + // m[name + ".raw"] = res; + // m[name + ".range"] = duration; + res /= duration; + return res; + }; + perf_map_10s[name] = load_perf_stats(perf_stat.perf_stat_[0], perf_map_10s); + perf_map_10m[name] = load_perf_stats(perf_stat.perf_stat_[1], perf_map_10m); + + auto current_duration = (double(now_ticks - begin_ticks_) * estimated_inv_ticks_per_second); + if (td::ends_with(name, ".duration")) { + value *= estimated_inv_ticks_per_second; + } + current_perf_map[name] = double(value) / current_duration; + // current_perf_map[name + ".raw"] = double(value); + // current_perf_map[name + ".range"] = double(now_ticks - begin_ticks_) * estimated_inv_ticks_per_second; + }; + + td::StringBuilder sb; + sb << "================================= PERF COUNTERS ================================\n"; + sb << "ticks_per_second_estimate\t" << 1.0 / estimated_inv_ticks_per_second << "\n"; + for (auto &it : perf_map_10s) { + const std::string &name = it.first; + auto dot_at = name.rfind('.'); + CHECK(dot_at != std::string::npos); + auto base_name = name.substr(0, dot_at); + auto rest_name = name.substr(dot_at + 1); + td::Slice new_rest_name = rest_name; + if (rest_name == "count") { + new_rest_name = "qps"; + } + if (rest_name == "duration") { + new_rest_name = "load"; + } + auto rewrite_name = PSTRING() << base_name << "." << new_rest_name; + sb << rewrite_name << "\t" << perf_map_10s[name] << " " << perf_map_10m[name] << " " << current_perf_map[name] + << "\n"; + } + sb << "\n"; + sb << "================================= ACTORS STATS =================================\n"; + double max_delay = 0; + ActorTypeStat sum_stat_forever; + ActorTypeStat sum_stat_10m; + ActorTypeStat sum_stat_10s; + for (auto &it : stats_forever) { + sum_stat_forever += it.second; + } + for (auto &it : stats_10m) { + sum_stat_10m += it.second; + } + for (auto &it : stats_10s) { + sum_stat_10s += it.second; + } + sb << "\n"; + + auto do_describe = [&](auto &&sb, const ActorTypeStat &stat_10s, const ActorTypeStat &stat_10m, + const ActorTypeStat &stat_forever) { + sb() << "load_per_second:\t" << stat_10s.seconds << " " << stat_10m.seconds << " " << stat_forever.seconds << "\n"; + sb() << "messages_per_second:\t" << stat_10s.messages << " " << stat_10m.messages << " " << stat_forever.messages + << "\n"; + + sb() << "max_execute_messages:\t" << stat_forever.max_execute_messages.value_10s << " " + << stat_forever.max_execute_messages.value_10m << " " << stat_forever.max_execute_messages.value_forever + << "\n"; + + sb() << "max_execute_seconds:\t" << stat_forever.max_execute_seconds.value_10s << "s" + << " " << stat_forever.max_execute_seconds.value_10m << "s" + << " " << stat_forever.max_execute_seconds.value_forever << "s\n"; + sb() << "max_message_seconds:\t" << stat_forever.max_message_seconds.value_10s << " " + << stat_forever.max_message_seconds.value_10m << " " << stat_forever.max_message_seconds.value_forever << "\n"; + sb() << "created_per_second:\t" << stat_10s.created << " " << stat_10m.created << " " << stat_forever.created + << "\n"; + + auto executing_for = + stat_forever.executing_start > 1e15 + ? 0 + : double(td::Clocks::rdtsc()) * estimated_inv_ticks_per_second - stat_forever.executing_start; + sb() << "max_delay:\t" << stat_forever.max_delay_seconds.value_10s << "s " + << stat_forever.max_delay_seconds.value_10m << "s " << stat_forever.max_delay_seconds.value_forever << "s\n"; + sb() << "" + << "alive: " << stat_forever.alive << " executing: " << stat_forever.executing + << " max_executing_for: " << executing_for << "s\n"; + }; + + auto describe = [&](td::StringBuilder &sb, std::type_index actor_type_index) { + auto stat_10s = stats_10s[actor_type_index]; + auto stat_10m = stats_10m[actor_type_index]; + auto stat_forever = stats_forever[actor_type_index]; + do_describe([&sb]() -> td::StringBuilder & { return sb << "\t\t"; }, stat_10s, stat_10m, stat_forever); + }; + + sb << "Cummulative stats:\n"; + do_describe([&sb]() -> td::StringBuilder & { return sb << "\t"; }, sum_stat_10s, sum_stat_10m, sum_stat_forever); + sb << "\n"; + + auto top_k_by = [&](auto &stats_map, size_t k, std::string description, auto by) { + auto stats = td::transform(stats_map, [](auto &it) { return std::make_pair(it.first, it.second); }); + k = std::min(k, stats.size()); + std::partial_sort(stats.begin(), stats.begin() + k, stats.end(), [&](auto &a, auto &b) { return by(a) > by(b); }); + bool is_first = true; + for (size_t i = 0; i < k; i++) { + auto value = by(stats[i]); + if (value < 1e-9) { + break; + } + if (is_first) { + sb << "top actors by " << description << "\n"; + is_first = false; + } + sb << "\t#" << i << ": " << ActorTypeStatManager::get_class_name(stats[i].first.name()) << "\t" << value << "\n"; + } + sb << "\n"; + }; + using Entry = std::pair; + static auto cutoff = [](auto x, auto min_value) { return x < min_value ? decltype(x){} : x; }; + top_k_by(stats_10s, 10, "load_10s", [](auto &x) { return cutoff(x.second.seconds, 0.005); }); + + top_k_by(stats_10m, 10, "load_10m", [](auto &x) { return cutoff(x.second.seconds, 0.005); }); + top_k_by(stats_forever, 10, "max_execute_seconds_10m", + [](Entry &x) { return cutoff(x.second.max_execute_seconds.value_10m, 0.5); }); + auto rdtsc_seconds = double(now_ticks) * estimated_inv_ticks_per_second; + top_k_by(stats_forever, 10, "executing_for", [&](Entry &x) { + if (x.second.executing_start > 1e15) { + return 0.0; + } + return rdtsc_seconds - x.second.executing_start; + }); + top_k_by(stats_forever, 10, "max_execute_messages_10m", + [](Entry &x) { return cutoff(x.second.max_execute_messages.value_10m, 10u); }); + + auto stats = td::transform(stats_forever, [](auto &it) { return std::make_pair(it.first, it.second); }); + + auto main_key = [&](std::type_index actor_type_index) { + auto stat_10s = stats_10s[actor_type_index]; + auto stat_10m = stats_10m[actor_type_index]; + auto stat_forever = stats_forever[actor_type_index]; + return std::make_tuple(cutoff(std::max(stat_10s.seconds, stat_10m.seconds), 0.1), + cutoff(stat_forever.max_execute_seconds.value_10m, 0.5), stat_forever.seconds); + }; + std::sort(stats.begin(), stats.end(), + [&](auto &left, auto &right) { return main_key(left.first) > main_key(right.first); }); + auto debug = Debug(SchedulerContext::get()->scheduler_group()); + debug.dump(sb); + sb << "All actors:\n"; + for (auto &it : stats) { + sb << "\t" << ActorTypeStatManager::get_class_name(it.first.name()) << "\n"; + auto key = main_key(it.first); + describe(sb, it.first); + } + sb << "\n"; + return sb.as_cslice().str(); +} +ActorStats::PefStat::PefStat() { + for (std::size_t i = 0; i < SIZE; i++) { + perf_stat_[i] = td::TimedStat>(DURATIONS[i], td::Time::now()); + perf_stat_[i].add_event(0, td::Time::now()); + } +} + +void ActorStats::update(td::Timestamp now) { + auto stat = td::actor::ActorTypeStatManager::get_stats(estimate_inv_ticks_per_second()); + for (auto &timed_stat : stat_) { + timed_stat.add_event(stat, now.at()); + } + NamedPerfCounter::get_default().for_each([&](td::Slice name, td::int64 value) { + auto &stat = pef_stats_[name.str()].perf_stat_; + for (auto &timed_stat : stat) { + timed_stat.add_event(value, now.at()); + } + }); +} +constexpr int ActorStats::DURATIONS[SIZE]; +constexpr const char *ActorStats::DESCR[SIZE]; +} // namespace actor +} // namespace td diff --git a/tdactor/td/actor/ActorStats.h b/tdactor/td/actor/ActorStats.h new file mode 100644 index 000000000..00f7ebeb7 --- /dev/null +++ b/tdactor/td/actor/ActorStats.h @@ -0,0 +1,52 @@ +#pragma once +#include "td/actor/actor.h" +#include "td/utils/TimedStat.h" +namespace td { +namespace actor { + +class ActorStats : public td::actor::Actor { + public: + ActorStats() { + } + void start_up() override; + double estimate_inv_ticks_per_second(); + std::string prepare_stats(); + + private: + template + struct StatStorer { + void on_event(const T &event) { + if (!first_) { + first_ = event; + first_ts_ = Clocks::rdtsc(); + } + } + double get_duration(double inv_ticks_per_second) const { + if (first_) { + return std::max(1.0, (Clocks::rdtsc() - first_ts_) * inv_ticks_per_second); + } + return 1.0; + } + td::optional first_; + td::uint64 first_ts_; + }; + static constexpr std::size_t SIZE = 2; + static constexpr const char *DESCR[SIZE] = {"10sec", "10min"}; + static constexpr int DURATIONS[SIZE] = {10, 10 * 60}; + td::TimedStat> stat_[SIZE]; + struct PefStat { + PefStat(); + td::TimedStat> perf_stat_[SIZE]; + }; + std::map pef_stats_; + td::Timestamp begin_ts_; + td::uint64 begin_ticks_{}; + void loop() override { + alarm_timestamp() = td::Timestamp::in(5.0); + update(td::Timestamp::now()); + } + void update(td::Timestamp now); +}; + +} // namespace actor +} // namespace td diff --git a/tdactor/td/actor/common.h b/tdactor/td/actor/common.h index d38589747..222db7436 100644 --- a/tdactor/td/actor/common.h +++ b/tdactor/td/actor/common.h @@ -19,6 +19,7 @@ #pragma once #include "td/actor/core/Actor.h" #include "td/actor/core/ActorSignals.h" +#include "td/actor/core/ActorTypeStat.h" #include "td/actor/core/SchedulerId.h" #include "td/actor/core/SchedulerContext.h" #include "td/actor/core/Scheduler.h" @@ -68,7 +69,7 @@ using core::set_debug; struct Debug { public: Debug() = default; - Debug(std::shared_ptr group_info) : group_info_(std::move(group_info)) { + Debug(core::SchedulerGroupInfo *group_info) : group_info_(group_info) { } template void for_each(F &&f) { @@ -80,18 +81,29 @@ struct Debug { } } - void dump() { - for_each([](core::Debug &debug) { + void dump(td::StringBuilder &sb) { + sb << "list of active actors with names:\n"; + 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); + sb << "\t\"" << info.name << "\" is active for " << Time::now() - info.start_at << "s\n"; } }); + sb << "\nsizes of cpu local queues:\n"; + for (auto &scheduler : group_info_->schedulers) { + for (size_t i = 0; i < scheduler.cpu_threads_count; i++) { + auto size = scheduler.cpu_local_queue[i].size(); + if (size != 0) { + sb << "\tcpu#" << i << " queue.size() = " << size << "\n"; + } + } + } + sb << "\n"; } private: - std::shared_ptr group_info_; + core::SchedulerGroupInfo *group_info_; }; class Scheduler { @@ -142,7 +154,7 @@ class Scheduler { } Debug get_debug() { - return Debug{group_info_}; + return Debug{group_info_.get()}; } bool run() { @@ -200,6 +212,10 @@ class Scheduler { } }; +using core::ActorTypeStat; +using core::ActorTypeStatManager; +using core::ActorTypeStats; + // Some helper functions. Not part of public interface and not part // of namespace core namespace detail { @@ -324,7 +340,7 @@ void send_closure_impl(ActorRef actor_ref, ClosureT &&closure) { } template -void send_closure(ActorRef actor_ref, ArgsT &&... args) { +void send_closure(ActorRef actor_ref, ArgsT &&...args) { send_closure_impl(actor_ref, create_immediate_closure(std::forward(args)...)); } @@ -365,7 +381,7 @@ void send_closure_with_promise_later(ActorRef actor_ref, ClosureT &&closure, Pro } template -void send_closure_later(ActorRef actor_ref, ArgsT &&... args) { +void send_closure_later(ActorRef actor_ref, ArgsT &&...args) { send_closure_later_impl(actor_ref, create_delayed_closure(std::forward(args)...)); } @@ -396,15 +412,17 @@ inline void send_signals_later(ActorRef actor_ref, ActorSignals signals) { inline void register_actor_info_ptr(core::ActorInfoPtr actor_info_ptr) { auto state = actor_info_ptr->state().get_flags_unsafe(); + actor_info_ptr->on_add_to_queue(); core::SchedulerContext::get()->add_to_queue(std::move(actor_info_ptr), state.get_scheduler_id(), !state.is_shared()); } template -core::ActorInfoPtr create_actor(core::ActorOptions &options, ArgsT &&... args) noexcept { +core::ActorInfoPtr create_actor(core::ActorOptions &options, ArgsT &&...args) noexcept { auto *scheduler_context = core::SchedulerContext::get(); if (!options.has_scheduler()) { options.on_scheduler(scheduler_context->get_scheduler_id()); } + options.with_actor_stat_id(core::ActorTypeStatImpl::get_unique_id()); auto res = scheduler_context->get_actor_info_creator().create(std::make_unique(std::forward(args)...), options); register_actor_info_ptr(res); diff --git a/tdactor/td/actor/core/ActorExecutor.cpp b/tdactor/td/actor/core/ActorExecutor.cpp index 267758d5e..d15422424 100644 --- a/tdactor/td/actor/core/ActorExecutor.cpp +++ b/tdactor/td/actor/core/ActorExecutor.cpp @@ -114,6 +114,8 @@ void ActorExecutor::start() noexcept { actor_execute_context_.set_actor(&actor_info_.actor()); + actor_stats_ = actor_info_.actor_type_stat(); + auto execute_timer = actor_stats_.create_execute_timer(); while (flush_one_signal(signals)) { if (actor_execute_context_.has_immediate_flags()) { return; @@ -175,6 +177,11 @@ void ActorExecutor::finish() noexcept { if (add_to_queue) { actor_info_ptr = actor_info_.actor().get_actor_info_ptr(); } + if (!flags().is_closed() && flags().is_in_queue()) { + // Must do it while we are locked, so to it optimistically + // we will add actor to queue after unlock OR we are already in a queue OR we will be closed + actor_info_.on_add_to_queue(); + } if (actor_locker_.try_unlock(flags())) { if (add_to_queue) { dispatcher_.add_to_queue(std::move(actor_info_ptr), flags().get_scheduler_id(), !flags().is_shared()); @@ -193,23 +200,31 @@ bool ActorExecutor::flush_one_signal(ActorSignals &signals) { } switch (signal) { //NB: Signals will be handled in order of their value. - // For clarity it conincides with order in this switch + // For clarity, it coincides with order in this switch case ActorSignals::Pause: actor_execute_context_.set_pause(); break; - case ActorSignals::Kill: + case ActorSignals::Kill: { + auto message_timer = actor_stats_.create_message_timer(); actor_execute_context_.set_stop(); break; - case ActorSignals::StartUp: + } + case ActorSignals::StartUp: { + auto message_timer = actor_stats_.create_message_timer(); + actor_stats_.created(); actor_info_.actor().start_up(); break; - case ActorSignals::Wakeup: + } + case ActorSignals::Wakeup: { + auto message_timer = actor_stats_.create_message_timer(); actor_info_.actor().wake_up(); break; + } case ActorSignals::Alarm: if (actor_execute_context_.get_alarm_timestamp() && actor_execute_context_.get_alarm_timestamp().is_in_past()) { actor_execute_context_.alarm_timestamp() = Timestamp::never(); actor_info_.set_alarm_timestamp(Timestamp::never()); + auto message_timer = actor_stats_.create_message_timer(); actor_info_.actor().alarm(); } break; @@ -245,6 +260,7 @@ bool ActorExecutor::flush_one_message() { } actor_execute_context_.set_link_token(message.get_link_token()); + auto message_timer = actor_stats_.create_message_timer(); message.run(); return true; } @@ -257,7 +273,9 @@ void ActorExecutor::flush_context_flags() { } flags_.set_closed(true); if (!flags_.get_signals().has_signal(ActorSignals::Signal::StartUp)) { + auto message_timer = actor_stats_.create_message_timer(); actor_info_.actor().tear_down(); + actor_stats_.destroyed(); } actor_info_.destroy_actor(); } else { diff --git a/tdactor/td/actor/core/ActorExecutor.h b/tdactor/td/actor/core/ActorExecutor.h index 366cb754d..dd86b5ae2 100644 --- a/tdactor/td/actor/core/ActorExecutor.h +++ b/tdactor/td/actor/core/ActorExecutor.h @@ -24,6 +24,7 @@ #include "td/actor/core/ActorMessage.h" #include "td/actor/core/ActorSignals.h" #include "td/actor/core/ActorState.h" +#include "td/actor/core/ActorTypeStat.h" #include "td/actor/core/SchedulerContext.h" #include "td/utils/format.h" @@ -95,6 +96,7 @@ class ActorExecutor { ActorInfo &actor_info_; SchedulerDispatcher &dispatcher_; Options options_; + ActorTypeStatRef actor_stats_; ActorLocker actor_locker_{&actor_info_.state(), ActorLocker::Options() .with_can_execute_paused(options_.from_queue) .with_is_shared(!options_.has_poll) diff --git a/tdactor/td/actor/core/ActorInfo.h b/tdactor/td/actor/core/ActorInfo.h index 974192938..690b06d9f 100644 --- a/tdactor/td/actor/core/ActorInfo.h +++ b/tdactor/td/actor/core/ActorInfo.h @@ -19,6 +19,7 @@ #pragma once #include "td/actor/core/ActorState.h" +#include "td/actor/core/ActorTypeStat.h" #include "td/actor/core/ActorMailbox.h" #include "td/utils/Heap.h" @@ -34,8 +35,8 @@ class ActorInfo; using ActorInfoPtr = SharedObjectPool::Ptr; class ActorInfo : private HeapNode, private ListNode { public: - ActorInfo(std::unique_ptr actor, ActorState::Flags state_flags, Slice name) - : actor_(std::move(actor)), name_(name.begin(), name.size()) { + ActorInfo(std::unique_ptr actor, ActorState::Flags state_flags, Slice name, td::uint32 actor_stat_id) + : actor_(std::move(actor)), name_(name.begin(), name.size()), actor_stat_id_(actor_stat_id) { state_.set_flags_unsafe(state_flags); VLOG(actor) << "Create actor [" << name_ << "]"; } @@ -58,6 +59,18 @@ class ActorInfo : private HeapNode, private ListNode { Actor *actor_ptr() const { return actor_.get(); } + // NB: must be called only when actor is locked + ActorTypeStatRef actor_type_stat() { + auto res = ActorTypeStatManager::get_actor_type_stat(actor_stat_id_, actor_.get()); + if (in_queue_since_) { + res.pop_from_queue(in_queue_since_); + in_queue_since_ = 0; + } + return res; + } + void on_add_to_queue() { + in_queue_since_ = td::Clocks::rdtsc(); + } void destroy_actor() { actor_.reset(); } @@ -103,6 +116,8 @@ class ActorInfo : private HeapNode, private ListNode { std::atomic alarm_timestamp_at_{0}; ActorInfoPtr pin_; + td::uint64 in_queue_since_{0}; + td::uint32 actor_stat_id_{0}; }; } // namespace core diff --git a/tdactor/td/actor/core/ActorInfoCreator.h b/tdactor/td/actor/core/ActorInfoCreator.h index 49c7611ae..d818dc631 100644 --- a/tdactor/td/actor/core/ActorInfoCreator.h +++ b/tdactor/td/actor/core/ActorInfoCreator.h @@ -46,10 +46,16 @@ class ActorInfoCreator { return *this; } + Options& with_actor_stat_id(td::uint32 new_id) { + actor_stat_id = new_id; + return *this; + } + private: friend class ActorInfoCreator; Slice name; SchedulerId scheduler_id; + td::uint32 actor_stat_id{0}; bool is_shared{true}; bool in_queue{true}; //TODO: rename @@ -65,7 +71,7 @@ class ActorInfoCreator { flags.set_in_queue(args.in_queue); flags.set_signals(ActorSignals::one(ActorSignals::StartUp)); - auto actor_info_ptr = pool_.alloc(std::move(actor), flags, args.name); + auto actor_info_ptr = pool_.alloc(std::move(actor), flags, args.name, args.actor_stat_id); actor_info_ptr->actor().set_actor_info_ptr(actor_info_ptr); return actor_info_ptr; } diff --git a/tdactor/td/actor/core/ActorTypeStat.cpp b/tdactor/td/actor/core/ActorTypeStat.cpp new file mode 100644 index 000000000..4de6f1054 --- /dev/null +++ b/tdactor/td/actor/core/ActorTypeStat.cpp @@ -0,0 +1,124 @@ +#include "td/actor/core/Actor.h" +#include "td/actor/core/ActorTypeStat.h" +#include "td/actor/core/Scheduler.h" +#include "td/utils/port/thread_local.h" +#include +#include +#include +#include +#include +#include + +#ifdef __has_include +#if __has_include() +#include +#define CXXABI_AVAILABLE 1 +#else +#define CXXABI_AVAILABLE 0 +#endif +#else +#define CXXABI_AVAILABLE 0 +#endif + +namespace td { +namespace actor { +namespace core { + +class ActorTypeStatRef; +struct ActorTypeStatsTlsEntry { + struct Entry { + std::unique_ptr stat; + std::optional o_type_index; + }; + std::vector by_id; + std::mutex mutex; + + template + void foreach_entry(F &&f) { + std::lock_guard guard(mutex); + for (auto &entry : by_id) { + f(entry); + } + } + ActorTypeStatRef get_actor_type_stat(td::uint32 id, Actor &actor) { + if (id >= by_id.size()) { + std::lock_guard guard(mutex); + by_id.resize(id + 1); + } + auto &entry = by_id.at(id); + if (!entry.o_type_index) { + std::lock_guard guard(mutex); + entry.o_type_index = std::type_index(typeid(actor)); + entry.stat = std::make_unique(); + } + return ActorTypeStatRef{entry.stat.get()}; + } +}; + +struct ActorTypeStatsRegistry { + std::mutex mutex; + std::vector> entries; + void registry_entry(std::shared_ptr entry) { + std::lock_guard guard(mutex); + entries.push_back(std::move(entry)); + } + template + void foreach_entry(F &&f) { + std::lock_guard guard(mutex); + for (auto &entry : entries) { + f(*entry); + } + } +}; + +ActorTypeStatsRegistry registry; + +struct ActorTypeStatsTlsEntryRef { + ActorTypeStatsTlsEntryRef() { + entry_ = std::make_shared(); + registry.registry_entry(entry_); + } + std::shared_ptr entry_; +}; + +static TD_THREAD_LOCAL ActorTypeStatsTlsEntryRef *actor_type_stats_tls_entry = nullptr; + +ActorTypeStatRef ActorTypeStatManager::get_actor_type_stat(td::uint32 id, Actor *actor) { + if (!actor || !need_debug()) { + return ActorTypeStatRef{nullptr}; + } + td::init_thread_local(actor_type_stats_tls_entry); + ActorTypeStatsTlsEntry &tls_entry = *actor_type_stats_tls_entry->entry_; + return tls_entry.get_actor_type_stat(id, *actor); +} + +std::string ActorTypeStatManager::get_class_name(const char *name) { +#if CXXABI_AVAILABLE + int status; + char *real_name = abi::__cxa_demangle(name, nullptr, nullptr, &status); + if (status < 0) { + return name; + } + + std::string result = real_name; + std::free(real_name); + return result; +#else + return name; +#endif +} + +ActorTypeStats ActorTypeStatManager::get_stats(double inv_ticks_per_second) { + std::map stats; + registry.foreach_entry([&](ActorTypeStatsTlsEntry &tls_entry) { + tls_entry.foreach_entry([&](ActorTypeStatsTlsEntry::Entry &entry) { + if (entry.o_type_index) { + stats[entry.o_type_index.value()] += entry.stat->to_stat(inv_ticks_per_second); + } + }); + }); + return ActorTypeStats{.stats = std::move(stats)}; +} +} // namespace core +} // namespace actor +} // namespace td diff --git a/tdactor/td/actor/core/ActorTypeStat.h b/tdactor/td/actor/core/ActorTypeStat.h new file mode 100644 index 000000000..5b0bd18dc --- /dev/null +++ b/tdactor/td/actor/core/ActorTypeStat.h @@ -0,0 +1,395 @@ +#pragma once +#include "td/utils/int_types.h" +#include "td/utils/port/Clocks.h" +#include +#include +#include + +namespace td { +namespace actor { +namespace core { +class Actor; + +struct ActorTypeStat { + // diff (speed) + double created{0}; + double executions{0}; + double messages{0}; + double seconds{0}; + + // current statistics + td::int64 alive{0}; + td::int32 executing{0}; + double executing_start{1e20}; + + // max statistics (TODO: recent_max) + template + struct MaxStatGroup { + T value_forever{}; + T value_10s{}; + T value_10m{}; + MaxStatGroup &operator+=(const MaxStatGroup &other) { + value_forever = std::max(value_forever, other.value_forever); + value_10s = std::max(value_10s, other.value_10s); + value_10m = std::max(value_10m, other.value_10m); + return *this; + } + }; + MaxStatGroup max_execute_messages; + MaxStatGroup max_message_seconds; + MaxStatGroup max_execute_seconds; + MaxStatGroup max_delay_seconds; + + ActorTypeStat &operator+=(const ActorTypeStat &other) { + created += other.created; + executions += other.executions; + messages += other.messages; + seconds += other.seconds; + + alive += other.alive; + executing += other.executing; + executing_start = std::min(other.executing_start, executing_start); + + max_execute_messages += other.max_execute_messages; + max_message_seconds += other.max_message_seconds; + max_execute_seconds += other.max_execute_seconds; + max_delay_seconds += other.max_delay_seconds; + return *this; + } + + ActorTypeStat &operator-=(const ActorTypeStat &other) { + created -= other.created; + executions -= other.executions; + messages -= other.messages; + seconds -= other.seconds; + return *this; + } + ActorTypeStat &operator/=(double t) { + if (t > 1e-2) { + created /= t; + executions /= t; + messages /= t; + seconds /= t; + } else { + created = 0; + executions = 0; + messages = 0; + seconds = 0; + } + return *this; + } +}; + +struct ActorTypeStatImpl { + public: + ActorTypeStatImpl() { + } + + class MessageTimer { + public: + MessageTimer(ActorTypeStatImpl *stat, td::uint64 started_at = Clocks::rdtsc()) + : stat_(stat), started_at_(started_at) { + } + MessageTimer(const MessageTimer &) = delete; + MessageTimer(MessageTimer &&) = delete; + MessageTimer &operator=(const MessageTimer &) = delete; + MessageTimer &operator=(MessageTimer &&) = delete; + ~MessageTimer() { + if (stat_) { + auto ts = td::Clocks::rdtsc(); + stat_->message_finish(ts, ts - started_at_); + } + } + + private: + ActorTypeStatImpl *stat_; + td::uint64 started_at_; + }; + void created() { + inc(total_created_); + inc(alive_); + } + void destroyed() { + dec(alive_); + } + MessageTimer create_run_timer() { + return MessageTimer{this}; + } + + void message_finish(td::uint64 ts, td::uint64 ticks) { + inc(total_messages_); + inc(execute_messages_); + add(total_ticks_, ticks); + max_message_ticks_.update(ts, ticks); + } + void on_delay(td::uint64 ts, td::uint64 ticks) { + max_delay_ticks_.update(ts, ticks); + } + + void execute_start(td::uint64 ts) { + // TODO: this is mostly protection for recursive actor calls, which curretly should be almost impossible + // But too full handle it, one would use one executing_cnt per thread, so only upper level execution is counted + if (inc(executing_) == 1) { + store(execute_start_, ts); + store(execute_messages_, 0); + } + } + void execute_finish(td::uint64 ts) { + CHECK(executing_ > 0); + if (dec(executing_) == 0) { + max_execute_messages_.update(ts, load(execute_messages_)); + max_execute_ticks_.update(ts, ts - load(execute_start_)); + + inc(total_executions_); + store(execute_start_, 0); + store(execute_messages_, 0); + } + } + + template + static td::uint32 get_unique_id() { + static td::uint32 value = get_next_unique_id(); + return value; + } + + static td::uint32 get_next_unique_id() { + static std::atomic next_id_{}; + return ++next_id_; + } + ActorTypeStat to_stat(double inv_ticks_per_second) const { + auto execute_start_copy = load(execute_start_); + auto actual_total_ticks = load(total_ticks_); + auto ts = Clocks::rdtsc(); + if (execute_start_copy != 0) { + actual_total_ticks += ts - execute_start_copy; + } + auto execute_start = ticks_to_seconds(load(execute_start_), inv_ticks_per_second); + return ActorTypeStat{.created = double(load(total_created_)), + .executions = double(load(total_executions_)), + .messages = double(load(total_messages_)), + .seconds = ticks_to_seconds(actual_total_ticks, inv_ticks_per_second), + + .alive = load(alive_), + .executing = load(executing_), + .executing_start = execute_start < 1e-9 ? 1e20 : execute_start, + .max_execute_messages = load(max_execute_messages_), + .max_message_seconds = load_seconds(max_message_ticks_, inv_ticks_per_second), + .max_execute_seconds = load_seconds(max_execute_ticks_, inv_ticks_per_second), + .max_delay_seconds = load_seconds(max_delay_ticks_, inv_ticks_per_second)}; + } + + private: + static double ticks_to_seconds(td::uint64 ticks, double inv_tick_per_second) { + return double(ticks) * inv_tick_per_second; + } + + template + static T load(const std::atomic &a) { + return a.load(std::memory_order_relaxed); + } + template + static void store(std::atomic &a, S value) { + a.store(value, std::memory_order_relaxed); + } + template + static T add(std::atomic &a, S value) { + T new_value = load(a) + value; + store(a, new_value); + return new_value; + } + template + static T inc(std::atomic &a) { + return add(a, 1); + } + + template + static T dec(std::atomic &a) { + return add(a, -1); + } + template + static void relax_max(std::atomic &a, T value) { + auto old_value = load(a); + if (value > old_value) { + store(a, value); + } + } + + template + class MaxCounter { + alignas(64) std::atomic max_values[2] = {0}; + std::atomic last_update_segment_time = 0; + + void update_current_segment(uint64 current_segment_time, uint64 segment_difference) { + if (segment_difference >= 2) { + store(max_values[0], 0); + store(max_values[1], 0); + } else if (segment_difference == 1) { + store(max_values[1 - (current_segment_time & 1)], 0); + } + store(last_update_segment_time, current_segment_time); + } + + public: + inline void update(td::uint64 rdtsc, ValueT value) { + auto current_segment_time = rdtsc / (Clocks::rdtsc_frequency() * Interval); + + auto segment_difference = current_segment_time - last_update_segment_time; + + if (unlikely(segment_difference != 0)) { + update_current_segment(current_segment_time, segment_difference); + } + + relax_max(max_values[current_segment_time & 1], value); + } + + inline ValueT get_max(uint64_t rdtsc) const { + uint64_t current_segment_time = rdtsc / (Clocks::rdtsc_frequency() * Interval); + uint64_t segment_difference = current_segment_time - load(last_update_segment_time); + + if (segment_difference >= 2) { + return 0; + } else if (segment_difference == 1) { + return load(max_values[current_segment_time & 1]); + } else { + return std::max(load(max_values[0]), load(max_values[1])); + } + } + }; + + template + struct MaxCounterGroup { + std::atomic max_forever{}; + MaxCounter max_10m; + MaxCounter max_10s; + + inline void update(td::uint64 rdtsc, T value) { + relax_max(max_forever, value); + max_10m.update(rdtsc, value); + max_10s.update(rdtsc, value); + } + }; + template + static ActorTypeStat::MaxStatGroup load(const MaxCounterGroup &src) { + auto ts = Clocks::rdtsc(); + return {.value_forever = load(src.max_forever), + .value_10s = src.max_10s.get_max(ts), + .value_10m = src.max_10m.get_max(ts)}; + } + template + static ActorTypeStat::MaxStatGroup load_seconds(const MaxCounterGroup &src, double inv_ticks_per_second) { + auto ts = Clocks::rdtsc(); + return {.value_forever = ticks_to_seconds(load(src.max_forever), inv_ticks_per_second), + .value_10s = ticks_to_seconds(src.max_10s.get_max(ts), inv_ticks_per_second), + .value_10m = ticks_to_seconds(src.max_10m.get_max(ts), inv_ticks_per_second)}; + } + + // total (increment only statistics) + std::atomic total_created_{0}; + std::atomic total_executions_{0}; + std::atomic total_messages_{0}; + std::atomic total_ticks_{0}; + + // current statistics + std::atomic alive_{0}; + std::atomic executing_{0}; + + // max statistics (TODO: recent_max) + MaxCounterGroup max_execute_messages_; + MaxCounterGroup max_message_ticks_; + MaxCounterGroup max_execute_ticks_; + MaxCounterGroup max_delay_ticks_; + + // execute state + std::atomic execute_start_{0}; + std::atomic execute_messages_{0}; +}; + +class ActorTypeStatRef { + public: + ActorTypeStatImpl *ref_{nullptr}; + + void created() { + if (!ref_) { + return; + } + ref_->created(); + } + void destroyed() { + if (!ref_) { + return; + } + ref_->destroyed(); + } + void pop_from_queue(td::uint64 in_queue_since) { + if (!ref_) { + return; + } + CHECK(in_queue_since); + auto ts = td::Clocks::rdtsc(); + ref_->on_delay(ts, ts - in_queue_since); + } + void start_execute() { + if (!ref_) { + return; + } + ref_->execute_start(td::Clocks::rdtsc()); + } + void finish_execute() { + if (!ref_) { + return; + } + ref_->execute_finish(td::Clocks::rdtsc()); + } + ActorTypeStatImpl::MessageTimer create_message_timer() { + if (!ref_) { + return ActorTypeStatImpl::MessageTimer{nullptr, 0}; + } + return ActorTypeStatImpl::MessageTimer{ref_}; + } + + struct ExecuteTimer { + ExecuteTimer() = delete; + ExecuteTimer(const ExecuteTimer &) = delete; + ExecuteTimer(ExecuteTimer &&) = delete; + ExecuteTimer &operator=(const ExecuteTimer &) = delete; + ExecuteTimer &operator=(ExecuteTimer &&) = delete; + + ExecuteTimer(ActorTypeStatRef *stat) : stat(stat) { + stat->start_execute(); + } + ActorTypeStatRef *stat{}; + ~ExecuteTimer() { + stat->finish_execute(); + } + }; + ExecuteTimer create_execute_timer() { + return ExecuteTimer(this); + } +}; + +// TODO: currently it is implemented via TD_THREAD_LOCAL, so the statistics is global across different schedulers +struct ActorTypeStats { + std::map stats; + ActorTypeStats &operator-=(const ActorTypeStats &other) { + for (auto &it : other.stats) { + stats.at(it.first) -= it.second; + } + return *this; + } + ActorTypeStats &operator/=(double x) { + for (auto &it : stats) { + it.second /= x; + } + return *this; + } +}; +class ActorTypeStatManager { + public: + static ActorTypeStatRef get_actor_type_stat(td::uint32 id, Actor *actor); + static ActorTypeStats get_stats(double inv_ticks_per_second); + static std::string get_class_name(const char *name); +}; + +} // namespace core +} // namespace actor +} // namespace td \ No newline at end of file diff --git a/tdactor/td/actor/core/Scheduler.h b/tdactor/td/actor/core/Scheduler.h index 3de519e09..e76b919e1 100644 --- a/tdactor/td/actor/core/Scheduler.h +++ b/tdactor/td/actor/core/Scheduler.h @@ -130,27 +130,20 @@ struct LocalQueue { public: template bool push(T value, F &&overflow_f) { - auto res = std::move(next_); - next_ = std::move(value); - if (res) { - queue_.local_push(res.unwrap(), overflow_f); - return true; - } - return false; + queue_.local_push(std::move(value), overflow_f); + return true; } bool try_pop(T &message) { - if (!next_) { - return queue_.local_pop(message); - } - message = next_.unwrap(); - return true; + return queue_.local_pop(message); } - bool steal(T &message, LocalQueue &other) { + bool steal(T &message, LocalQueue &other) { return queue_.steal(message, other.queue_); } + size_t size() const { + return queue_.size(); + } private: - td::optional next_; StealingQueue queue_; char pad[TD_CONCURRENCY_PAD - sizeof(optional)]; }; @@ -267,11 +260,12 @@ class Scheduler { bool is_stop_requested() override; void stop() override; - private: - SchedulerGroupInfo *scheduler_group() const { + SchedulerGroupInfo *scheduler_group() const override { return scheduler_group_; } + private: + ActorInfoCreator *creator_; SchedulerId scheduler_id_; CpuWorkerId cpu_worker_id_; diff --git a/tdactor/td/actor/core/SchedulerContext.h b/tdactor/td/actor/core/SchedulerContext.h index e46aef7d5..99b1922f5 100644 --- a/tdactor/td/actor/core/SchedulerContext.h +++ b/tdactor/td/actor/core/SchedulerContext.h @@ -38,6 +38,7 @@ class SchedulerDispatcher { }; struct Debug; +struct SchedulerGroupInfo; class SchedulerContext : public Context, public SchedulerDispatcher { public: virtual ~SchedulerContext() = default; @@ -59,6 +60,7 @@ class SchedulerContext : public Context, public SchedulerDispa // Debug virtual Debug &get_debug() = 0; + virtual SchedulerGroupInfo *scheduler_group() const = 0; }; } // namespace core } // namespace actor diff --git a/tdactor/test/actors_core.cpp b/tdactor/test/actors_core.cpp index 96cd6239e..1c56d1e5b 100644 --- a/tdactor/test/actors_core.cpp +++ b/tdactor/test/actors_core.cpp @@ -19,15 +19,20 @@ #include "td/actor/core/ActorLocker.h" #include "td/actor/actor.h" #include "td/actor/PromiseFuture.h" +#include "td/actor/ActorStats.h" #include "td/utils/format.h" #include "td/utils/logging.h" +#include "td/utils/misc.h" +#include "td/utils/port/thread.h" #include "td/utils/port/thread.h" #include "td/utils/Random.h" #include "td/utils/Slice.h" #include "td/utils/StringBuilder.h" #include "td/utils/tests.h" #include "td/utils/Time.h" +#include "td/utils/TimedStat.h" +#include "td/utils/port/sleep.h" #include #include @@ -1102,4 +1107,59 @@ TEST(Actor2, send_vs_close2) { scheduler.run(); } } + +TEST(Actor2, test_stats) { + Scheduler scheduler({8}); + td::actor::set_debug(true); + + auto watcher = td::create_shared_destructor([] { SchedulerContext::get()->stop(); }); + scheduler.run_in_context([watcher = std::move(watcher)] { + class SleepWorker : public Actor { + void loop() override { + // 0.8 load + td::usleep_for(800000); + alarm_timestamp() = td::Timestamp::in(0.2); + } + }; + class QueueWorker : public Actor { + void loop() override { + for (int i = 0; i < 20; i++) { + send_closure(actor_id(this), &QueueWorker::ping); + } + alarm_timestamp() = td::Timestamp::in(1.0); + } + void ping() { + } + }; + class Master : public Actor { + public: + Master(std::shared_ptr watcher) : watcher_(std::move(watcher)) { + } + void start_up() override { + alarm_timestamp() = td::Timestamp::in(1); + stats_ = td::actor::create_actor("actor_stats"); + td::actor::create_actor("sleep_worker").release(); + td::actor::create_actor("queue_worker").release(); + } + void alarm() override { + td::actor::send_closure(stats_, &ActorStats::prepare_stats, td::promise_send_closure(actor_id(this), &Master::on_stats)); + alarm_timestamp() = td::Timestamp::in(5); + } + void on_stats(td::Result r_stats) { + LOG(ERROR) << "\n" << r_stats.ok(); + if (--cnt_ == 0) { + stop(); + } + } + + private: + std::shared_ptr watcher_; + td::actor::ActorOwn stats_; + int cnt_={2}; + }; + td::actor::create_actor("Master", watcher).release(); + }); + + scheduler.run(); +} #endif //!TD_THREAD_UNSUPPORTED diff --git a/tddb/CMakeLists.txt b/tddb/CMakeLists.txt index 89730b954..55476e648 100644 --- a/tddb/CMakeLists.txt +++ b/tddb/CMakeLists.txt @@ -59,7 +59,7 @@ target_link_libraries(tddb PUBLIC tdutils tdactor tddb_utils) if (TDDB_USE_ROCKSDB) target_sources(tddb PRIVATE ${TDDB_ROCKSDB_SOURCE}) target_compile_definitions(tddb PUBLIC -DTDDB_USE_ROCKSDB) - target_link_libraries(tddb PRIVATE rocksdb) + target_link_libraries(tddb PUBLIC rocksdb) target_include_directories(tddb PRIVATE $) endif() diff --git a/tddb/td/db/KeyValue.h b/tddb/td/db/KeyValue.h index 980e367c6..12c3a4f8d 100644 --- a/tddb/td/db/KeyValue.h +++ b/tddb/td/db/KeyValue.h @@ -32,6 +32,9 @@ class KeyValueReader { virtual Status for_each(std::function f) { return Status::Error("for_each is not supported"); } + virtual Status for_each_in_range (Slice begin, Slice end, std::function f) { + return td::Status::Error("foreach_range is not supported"); + } }; class PrefixedKeyValueReader : public KeyValueReader { diff --git a/tddb/td/db/MemoryKeyValue.cpp b/tddb/td/db/MemoryKeyValue.cpp index aaf5472d7..080133602 100644 --- a/tddb/td/db/MemoryKeyValue.cpp +++ b/tddb/td/db/MemoryKeyValue.cpp @@ -29,6 +29,24 @@ Result MemoryKeyValue::get(Slice key, std::string &va value = it->second; return GetStatus::Ok; } + +Status MemoryKeyValue::for_each(std::function f) { + for (auto &it : map_) { + TRY_STATUS(f(it.first, it.second)); + } + return Status::OK(); +} + +Status MemoryKeyValue::for_each_in_range(Slice begin, Slice end, std::function f) { + for (auto it = map_.lower_bound(begin); it != map_.end(); it++) { + if (it->first < end) { + TRY_STATUS(f(it->first, it->second)); + } else { + break; + } + } + return Status::OK(); +} Status MemoryKeyValue::set(Slice key, Slice value) { map_[key.str()] = value.str(); return Status::OK(); diff --git a/tddb/td/db/MemoryKeyValue.h b/tddb/td/db/MemoryKeyValue.h index c9d584bcf..f0b5faa08 100644 --- a/tddb/td/db/MemoryKeyValue.h +++ b/tddb/td/db/MemoryKeyValue.h @@ -25,6 +25,8 @@ namespace td { class MemoryKeyValue : public KeyValue { public: Result get(Slice key, std::string &value) override; + Status for_each(std::function f) override; + Status for_each_in_range(Slice begin, Slice end, std::function f) override; Status set(Slice key, Slice value) override; Status erase(Slice key) override; Result count(Slice prefix) override; diff --git a/tddb/td/db/RocksDb.cpp b/tddb/td/db/RocksDb.cpp index f1af0cf79..f1aa64a5d 100644 --- a/tddb/td/db/RocksDb.cpp +++ b/tddb/td/db/RocksDb.cpp @@ -65,12 +65,16 @@ Result RocksDb::open(std::string path, RocksDbOptions options) { rocksdb::Options db_options; static auto default_cache = rocksdb::NewLRUCache(1 << 30); - if (options.block_cache == nullptr) { + if (!options.no_block_cache && options.block_cache == nullptr) { options.block_cache = default_cache; } rocksdb::BlockBasedTableOptions table_options; - table_options.block_cache = options.block_cache; + if (options.no_block_cache) { + table_options.no_block_cache = true; + } else { + table_options.block_cache = options.block_cache; + } db_options.table_factory.reset(rocksdb::NewBlockBasedTableFactory(table_options)); db_options.use_direct_reads = options.use_direct_reads; @@ -81,6 +85,8 @@ Result RocksDb::open(std::string path, RocksDbOptions options) { db_options.bytes_per_sync = 1 << 20; db_options.writable_file_max_buffer_size = 2 << 14; db_options.statistics = options.statistics; + db_options.max_log_file_size = 100 << 20; + db_options.keep_log_file_num = 1; rocksdb::OptimisticTransactionDBOptions occ_options; occ_options.validate_policy = rocksdb::OccValidationPolicy::kValidateSerial; rocksdb::ColumnFamilyOptions cf_options(db_options); @@ -212,6 +218,32 @@ Status RocksDb::for_each(std::function f) { return Status::OK(); } +Status RocksDb::for_each_in_range(Slice begin, Slice end, std::function f) { + rocksdb::ReadOptions options; + options.snapshot = snapshot_.get(); + std::unique_ptr iterator; + if (snapshot_ || !transaction_) { + iterator.reset(db_->NewIterator(options)); + } else { + iterator.reset(transaction_->GetIterator(options)); + } + + auto comparator = rocksdb::BytewiseComparator(); + iterator->Seek(to_rocksdb(begin)); + for (; iterator->Valid(); iterator->Next()) { + auto key = from_rocksdb(iterator->key()); + if (comparator->Compare(to_rocksdb(key), to_rocksdb(end)) >= 0) { + break; + } + auto value = from_rocksdb(iterator->value()); + TRY_STATUS(f(key, value)); + } + if (!iterator->status().ok()) { + return from_rocksdb(iterator->status()); + } + return td::Status::OK(); +} + Status RocksDb::begin_write_batch() { CHECK(!transaction_); write_batch_ = std::make_unique(); diff --git a/tddb/td/db/RocksDb.h b/tddb/td/db/RocksDb.h index 32c53a529..499a33281 100644 --- a/tddb/td/db/RocksDb.h +++ b/tddb/td/db/RocksDb.h @@ -23,6 +23,7 @@ #endif #include "td/db/KeyValue.h" +#include "td/utils/Span.h" #include "td/utils/Status.h" #include "td/utils/optional.h" @@ -32,6 +33,8 @@ #include #include +#include + namespace rocksdb { class Cache; class OptimisticTransactionDB; @@ -59,6 +62,7 @@ struct RocksDbOptions { std::shared_ptr block_cache; // Default - one 1G cache for all RocksDb std::shared_ptr snapshot_statistics = nullptr; bool use_direct_reads = false; + bool no_block_cache = false; }; class RocksDb : public KeyValue { @@ -72,6 +76,7 @@ class RocksDb : public KeyValue { Status erase(Slice key) override; Result count(Slice prefix) override; Status for_each(std::function f) override; + Status for_each_in_range (Slice begin, Slice end, std::function f) override; Status begin_write_batch() override; Status commit_write_batch() override; diff --git a/tddb/td/db/utils/StreamToFileActor.cpp b/tddb/td/db/utils/StreamToFileActor.cpp index 5a3427d38..fb733d02f 100644 --- a/tddb/td/db/utils/StreamToFileActor.cpp +++ b/tddb/td/db/utils/StreamToFileActor.cpp @@ -81,7 +81,7 @@ Result StreamToFileActor::do_loop() { sync_at_ = {}; } - bool need_update = sync_state_.set_synced_size(synced_size_) | sync_state_.set_flushed_size(flushed_size_); + bool need_update = int(sync_state_.set_synced_size(synced_size_)) | int(sync_state_.set_flushed_size(flushed_size_)); if (need_update && callback_) { callback_->on_sync_state_changed(); } diff --git a/tdfec/td/fec/raptorq/Solver.cpp b/tdfec/td/fec/raptorq/Solver.cpp index 02d8102fe..772271fa7 100644 --- a/tdfec/td/fec/raptorq/Solver.cpp +++ b/tdfec/td/fec/raptorq/Solver.cpp @@ -19,6 +19,7 @@ #include "td/fec/raptorq/Solver.h" #include "td/fec/algebra/GaussianElimination.h" #include "td/fec/algebra/InactivationDecoding.h" +#include "td/utils/ThreadSafeCounter.h" #include "td/utils/Timer.h" #include @@ -70,6 +71,7 @@ Result Solver::run(const Rfc::Parameters &p, Span symbol auto C = GaussianElimination::run(std::move(A), std::move(D)); return C; } + TD_PERF_COUNTER(raptor_solve); PerfWarningTimer x("solve"); Timer timer; auto perf_log = [&](Slice message) { diff --git a/tdtl/td/tl/tl_writer.cpp b/tdtl/td/tl/tl_writer.cpp index 5c672f264..f4f5f27cc 100644 --- a/tdtl/td/tl/tl_writer.cpp +++ b/tdtl/td/tl/tl_writer.cpp @@ -28,7 +28,7 @@ namespace tl { std::string TL_writer::int_to_string(int x) { char buf[15]; - std::sprintf(buf, "%d", x); + std::snprintf(buf, sizeof(buf), "%d", x); return buf; } diff --git a/tdutils/CMakeLists.txt b/tdutils/CMakeLists.txt index 4c7132470..f8c191a14 100644 --- a/tdutils/CMakeLists.txt +++ b/tdutils/CMakeLists.txt @@ -356,6 +356,7 @@ if (LZ4_FOUND) message(STATUS "Found LZ4 ${LZ4_LIBRARIES} ${LZ4_INCLUDE_DIRS}") target_link_libraries(tdutils PRIVATE ${LZ4_LIBRARIES}) target_include_directories(tdutils SYSTEM PRIVATE ${LZ4_INCLUDE_DIRS}) + target_link_directories(tdutils PUBLIC ${LZ4_LIBRARY_DIRS}) endif() if (ABSL_FOUND) diff --git a/tdutils/td/utils/HashMap.h b/tdutils/td/utils/HashMap.h index 9646bef90..1380f0291 100644 --- a/tdutils/td/utils/HashMap.h +++ b/tdutils/td/utils/HashMap.h @@ -22,6 +22,7 @@ #if TD_HAVE_ABSL #include +#include #else #include #endif @@ -31,9 +32,13 @@ namespace td { #if TD_HAVE_ABSL template > using HashMap = absl::flat_hash_map; +template , class E = std::equal_to<>> +using NodeHashMap = absl::node_hash_map; #else template > using HashMap = std::unordered_map; +template > +using NodeHashMap = std::unordered_map; #endif } // namespace td diff --git a/tdutils/td/utils/HashSet.h b/tdutils/td/utils/HashSet.h index b00d5a922..85254b325 100644 --- a/tdutils/td/utils/HashSet.h +++ b/tdutils/td/utils/HashSet.h @@ -22,6 +22,7 @@ #if TD_HAVE_ABSL #include +#include #else #include #endif @@ -29,11 +30,15 @@ namespace td { #if TD_HAVE_ABSL -template > -using HashSet = absl::flat_hash_set; +template , class E = std::equal_to<>> +using HashSet = absl::flat_hash_set; +template , class E = std::equal_to<>> +using NodeHashSet = absl::node_hash_set; #else -template > -using HashSet = std::unordered_set; +template , class E = std::equal_to<>> +using HashSet = std::unordered_set; +template , class E = std::equal_to<>> +using NodeHashSet = HashSet; #endif } // namespace td diff --git a/tdutils/td/utils/HazardPointers.h b/tdutils/td/utils/HazardPointers.h index 954d6efdc..7465140a5 100644 --- a/tdutils/td/utils/HazardPointers.h +++ b/tdutils/td/utils/HazardPointers.h @@ -32,12 +32,7 @@ class HazardPointers { explicit HazardPointers(size_t threads_n) : threads_(threads_n) { for (auto &data : threads_) { for (auto &ptr : data.hazard_) { -// workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64658 -#if TD_GCC && GCC_VERSION <= 40902 ptr = nullptr; -#else - std::atomic_init(&ptr, static_cast(nullptr)); -#endif } } } diff --git a/tdutils/td/utils/Status.h b/tdutils/td/utils/Status.h index 8bc210dba..cff808143 100644 --- a/tdutils/td/utils/Status.h +++ b/tdutils/td/utils/Status.h @@ -80,7 +80,7 @@ TRY_RESULT_PREFIX_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result, prefix) #define TRY_RESULT_PREFIX_ASSIGN(name, result, prefix) \ - TRY_RESULT_PREFIX_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result, prefix) + TRY_RESULT_PREFIX_IMPL(TD_CONCAT(r_response, __LINE__), name, result, prefix) #define TRY_RESULT_PROMISE_PREFIX(promise_name, name, result, prefix) \ TRY_RESULT_PROMISE_PREFIX_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result, prefix) diff --git a/tdutils/td/utils/StealingQueue.h b/tdutils/td/utils/StealingQueue.h index 20a39dc06..86c2f0ef6 100644 --- a/tdutils/td/utils/StealingQueue.h +++ b/tdutils/td/utils/StealingQueue.h @@ -115,6 +115,22 @@ class StealingQueue { std::atomic_thread_fence(std::memory_order_seq_cst); } + size_t size() const { + while (true) { + auto head = head_.load(); + auto tail = tail_.load(std::memory_order_acquire); + + if (tail < head) { + continue; + } + size_t n = tail - head; + if (n > N) { + continue; + } + return n; + } + } + private: std::atomic head_{0}; std::atomic tail_{0}; diff --git a/tdutils/td/utils/ThreadSafeCounter.h b/tdutils/td/utils/ThreadSafeCounter.h index 55bf94b5a..aa976b2fb 100644 --- a/tdutils/td/utils/ThreadSafeCounter.h +++ b/tdutils/td/utils/ThreadSafeCounter.h @@ -137,4 +137,55 @@ class NamedThreadSafeCounter { Counter counter_; }; +// another class for simplicity, it +struct NamedPerfCounter { + public: + static NamedPerfCounter &get_default() { + static NamedPerfCounter res; + return res; + } + struct PerfCounterRef { + NamedThreadSafeCounter::CounterRef count; + NamedThreadSafeCounter::CounterRef duration; + }; + PerfCounterRef get_counter(Slice name) { + return {.count = counter_.get_counter(PSLICE() << name << ".count"), + .duration = counter_.get_counter(PSLICE() << name << ".duration")}; + } + + struct ScopedPerfCounterRef : public NoCopyOrMove { + PerfCounterRef perf_counter; + uint64 started_at_ticks{td::Clocks::rdtsc()}; + + ~ScopedPerfCounterRef() { + perf_counter.count.add(1); + perf_counter.duration.add(td::Clocks::rdtsc() - started_at_ticks); + } + }; + + template + void for_each(F &&f) const { + counter_.for_each(f); + } + + void clear() { + counter_.clear(); + } + + friend StringBuilder &operator<<(StringBuilder &sb, const NamedPerfCounter &counter) { + return sb << counter.counter_; + } + private: + NamedThreadSafeCounter counter_; +}; + } // namespace td + +#define TD_PERF_COUNTER(name) \ + static auto perf_##name = td::NamedPerfCounter::get_default().get_counter(td::Slice(#name)); \ + auto scoped_perf_##name = td::NamedPerfCounter::ScopedPerfCounterRef{.perf_counter = perf_##name}; + +#define TD_PERF_COUNTER_SINCE(name, since) \ + static auto perf_##name = td::NamedPerfCounter::get_default().get_counter(td::Slice(#name)); \ + auto scoped_perf_##name = \ + td::NamedPerfCounter::ScopedPerfCounterRef{.perf_counter = perf_##name, .started_at_ticks = since}; diff --git a/tdutils/td/utils/common.h b/tdutils/td/utils/common.h index 79d3dc52a..f9f86c5c1 100644 --- a/tdutils/td/utils/common.h +++ b/tdutils/td/utils/common.h @@ -127,4 +127,12 @@ struct Auto { } }; +struct NoCopyOrMove { + NoCopyOrMove() = default; + NoCopyOrMove(NoCopyOrMove &&) = delete; + NoCopyOrMove(const NoCopyOrMove &) = delete; + NoCopyOrMove &operator=(NoCopyOrMove &&) = delete; + NoCopyOrMove &operator=(const NoCopyOrMove &) = delete; +}; + } // namespace td diff --git a/tdutils/td/utils/logging.cpp b/tdutils/td/utils/logging.cpp index 477823b89..345615f12 100644 --- a/tdutils/td/utils/logging.cpp +++ b/tdutils/td/utils/logging.cpp @@ -18,6 +18,7 @@ */ #include "td/utils/logging.h" +#include "ThreadSafeCounter.h" #include "td/utils/port/Clocks.h" #include "td/utils/port/StdStreams.h" #include "td/utils/port/thread_local.h" @@ -127,6 +128,9 @@ Logger::~Logger() { slice = MutableCSlice(slice.begin(), slice.begin() + slice.size() - 1); } log_.append(slice, log_level_); + + // put stats here to avoid conflict with PSTRING and PSLICE + TD_PERF_COUNTER_SINCE(logger, start_at_); } else { log_.append(as_cslice(), log_level_); } @@ -301,5 +305,4 @@ ScopedDisableLog::~ScopedDisableLog() { set_verbosity_level(sdl_verbosity); } } - } // namespace td diff --git a/tdutils/td/utils/logging.h b/tdutils/td/utils/logging.h index e5278b4bc..d00fba154 100644 --- a/tdutils/td/utils/logging.h +++ b/tdutils/td/utils/logging.h @@ -40,6 +40,7 @@ #include "td/utils/Slice.h" #include "td/utils/StackAllocator.h" #include "td/utils/StringBuilder.h" +#include "td/utils/port/Clocks.h" #include #include @@ -251,7 +252,8 @@ class Logger { , log_(log) , sb_(buffer_.as_slice()) , options_(options) - , log_level_(log_level) { + , log_level_(log_level) + , start_at_(Clocks::rdtsc()) { } Logger(LogInterface &log, const LogOptions &options, int log_level, Slice file_name, int line_num, Slice comment); @@ -283,6 +285,7 @@ class Logger { StringBuilder sb_; const LogOptions &options_; int log_level_; + td::uint64 start_at_; }; namespace detail { @@ -346,5 +349,4 @@ class TsLog : public LogInterface { lock_.clear(std::memory_order_release); } }; - } // namespace td diff --git a/tdutils/td/utils/port/Clocks.cpp b/tdutils/td/utils/port/Clocks.cpp index 59e80f619..7e27d51a9 100644 --- a/tdutils/td/utils/port/Clocks.cpp +++ b/tdutils/td/utils/port/Clocks.cpp @@ -22,6 +22,10 @@ #include namespace td { +int64 Clocks::monotonic_nano() { + auto duration = std::chrono::steady_clock::now().time_since_epoch(); + return std::chrono::duration_cast(duration).count(); +} double Clocks::monotonic() { // TODO write system specific functions, because std::chrono::steady_clock is steady only under Windows diff --git a/tdutils/td/utils/port/Clocks.h b/tdutils/td/utils/port/Clocks.h index 2d7d9e0fd..b20543650 100644 --- a/tdutils/td/utils/port/Clocks.h +++ b/tdutils/td/utils/port/Clocks.h @@ -17,15 +17,89 @@ Copyright 2017-2020 Telegram Systems LLP */ #pragma once +#include "td/utils/int_types.h" namespace td { struct Clocks { + static int64 monotonic_nano(); + static double monotonic(); static double system(); static int tz_offset(); + +#if defined(__i386__) or defined(__M_IX86) + static uint64 rdtsc() { + unsigned long long int x; + __asm__ volatile("rdtsc" : "=A"(x)); + return x; + } + + static constexpr uint64 rdtsc_frequency() { + return 2000'000'000; + } + + static constexpr double ticks_per_second() { + return 2e9; + } + + static constexpr double inv_ticks_per_second() { + return 0.5e-9; + } +#elif defined(__x86_64__) or defined(__M_X64) + static uint64 rdtsc() { + unsigned hi, lo; + __asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi)); + return ((unsigned long long)lo) | (((unsigned long long)hi) << 32); + } + static constexpr uint64 rdtsc_frequency() { + return 2000'000'000; + } + + static constexpr double ticks_per_second() { + return 2e9; + } + + static constexpr double inv_ticks_per_second() { + return 0.5e-9; + } +#elif defined(__aarch64__) or defined(_M_ARM64) + static uint64 rdtsc() { + unsigned long long val; + asm volatile("mrs %0, cntvct_el0" : "=r"(val)); + return val; + } + static uint64 rdtsc_frequency() { + unsigned long long val; + asm volatile("mrs %0, cntfrq_el0" : "=r"(val)); + return val; + } + + static double ticks_per_second() { + return static_cast(rdtsc_frequency()); + } + + static double inv_ticks_per_second() { + return 1.0 / static_cast(rdtsc_frequency()); + } +#else + static uint64 rdtsc() { + return monotonic_nano(); + } + static uint64 rdtsc_frequency() { + return 1000'000'000; + } + + static double ticks_per_second() { + return static_cast(rdtsc_frequency()); + } + + static double inv_ticks_per_second() { + return 1.0 / static_cast(rdtsc_frequency()); + } +#endif }; } // namespace td diff --git a/tdutils/td/utils/port/Stat.cpp b/tdutils/td/utils/port/Stat.cpp index d7ec50f75..816d622e4 100644 --- a/tdutils/td/utils/port/Stat.cpp +++ b/tdutils/td/utils/port/Stat.cpp @@ -58,8 +58,7 @@ #define PSAPI_VERSION 1 #endif #include -#pragma comment( lib, "psapi.lib" ) - +#pragma comment(lib, "psapi.lib") #endif @@ -413,7 +412,7 @@ Result cpu_stat() { #endif } -Result get_total_ram() { +Result get_total_mem_stat() { #if TD_LINUX TRY_RESULT(fd, FileFd::open("/proc/meminfo", FileFd::Read)); SCOPE_EXIT { @@ -425,8 +424,10 @@ Result get_total_ram() { if (size >= TMEM_SIZE - 1) { return Status::Error("Failed for read /proc/meminfo"); } + TotalMemStat stat; mem[size] = 0; - const char* s = mem; + const char *s = mem; + size_t got = 0; while (*s) { const char *name_begin = s; while (*s != 0 && *s != '\n') { @@ -437,18 +438,28 @@ Result get_total_ram() { name_end++; } Slice name(name_begin, name_end); + td::uint64 *dest = nullptr; if (name == "MemTotal") { + dest = &stat.total_ram; + } else if (name == "MemAvailable") { + dest = &stat.available_ram; + } + if (dest != nullptr) { Slice value(name_end, s); if (!value.empty() && value[0] == ':') { value.remove_prefix(1); } value = trim(value); value = split(value).first; - TRY_RESULT_PREFIX(mem, to_integer_safe(value), "Invalid value of MemTotal"); + TRY_RESULT_PREFIX(mem, to_integer_safe(value), PSLICE() << "Invalid value of " << name); if (mem >= 1ULL << (64 - 10)) { return Status::Error("Invalid value of MemTotal"); } - return mem * 1024; + *dest = mem * 1024; + got++; + if (got == 2) { + return stat; + } } if (*s == 0) { break; diff --git a/tdutils/td/utils/port/Stat.h b/tdutils/td/utils/port/Stat.h index cd16ebb36..ab97be0f1 100644 --- a/tdutils/td/utils/port/Stat.h +++ b/tdutils/td/utils/port/Stat.h @@ -64,6 +64,10 @@ Status update_atime(CSlice path) TD_WARN_UNUSED_RESULT; #endif -Result get_total_ram() TD_WARN_UNUSED_RESULT; +struct TotalMemStat { + uint64 total_ram; + uint64 available_ram; +}; +Result get_total_mem_stat() TD_WARN_UNUSED_RESULT; } // namespace td diff --git a/test/test-overlay.cpp b/test/test-overlay.cpp new file mode 100644 index 000000000..31db3e780 --- /dev/null +++ b/test/test-overlay.cpp @@ -0,0 +1,450 @@ +/* + 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 . + + 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 "adnl/adnl-node-id.hpp" +#include "adnl/adnl.h" +#include "adnl/utils.hpp" +#include "adnl/adnl-test-loopback-implementation.h" +#include "auto/tl/ton_api.h" +#include "checksum.h" +#include "common/bitstring.h" +#include "dht/dht.h" +#include "keys/keys.hpp" +#include "overlay-manager.h" +#include "overlay.h" +#include "overlay-id.hpp" +#include "overlay/overlays.h" +#include "td/actor/actor.h" +#include "td/utils/OptionParser.h" +#include "td/utils/Status.h" +#include "td/utils/Time.h" +#include "td/utils/UInt.h" +#include "td/utils/buffer.h" +#include "td/utils/crypto.h" +#include "td/utils/filesystem.h" +#include "td/utils/format.h" +#include "td/utils/port/path.h" +#include "td/utils/Random.h" +#include "td/utils/port/signals.h" +#include "td/utils/port/FileFd.h" +#include "td/utils/overloaded.h" +#include "common/errorlog.h" +#include "tl-utils/common-utils.hpp" +#include "tl/TlObject.h" +#include +#include + +#if TD_DARWIN || TD_LINUX +#include +#endif +#include +#include + +#include + +struct Node { + ton::PrivateKey pk; + ton::PublicKeyHash id; + ton::PublicKey id_full; + ton::adnl::AdnlNodeIdShort adnl_id; + ton::adnl::AdnlNodeIdFull adnl_id_full; + bool can_receive; +}; + +static std::vector root_nodes; +static std::vector slave_nodes; +static std::vector all_nodes; +static td::uint32 total_nodes = 4; +static td::int32 node_slaves_cnt = 3; +static size_t remaining = 0; +static td::Bits256 bcast_hash; + +class Callback : public ton::overlay::Overlays::Callback { + public: + Callback(bool can_receive) : can_receive_(can_receive) { + } + void receive_message(ton::adnl::AdnlNodeIdShort src, ton::overlay::OverlayIdShort overlay_id, + td::BufferSlice data) override { + UNREACHABLE(); + } + void receive_query(ton::adnl::AdnlNodeIdShort src, ton::overlay::OverlayIdShort overlay_id, td::BufferSlice data, + td::Promise promise) override { + UNREACHABLE(); + } + void receive_broadcast(ton::PublicKeyHash src, ton::overlay::OverlayIdShort overlay_id, + td::BufferSlice data) override { + CHECK(can_receive_); + CHECK(td::sha256_bits256(data.as_slice()) == bcast_hash); + CHECK(remaining > 0); + remaining--; + } + + private: + bool can_receive_; +}; + +int main(int argc, char *argv[]) { + SET_VERBOSITY_LEVEL(verbosity_INFO); + td::set_default_failure_signal_handler().ensure(); + + std::string db_root_ = "tmp-dir-test-catchain"; + td::rmrf(db_root_).ignore(); + td::mkdir(db_root_).ensure(); + + td::set_default_failure_signal_handler().ensure(); + + td::actor::ActorOwn keyring; + td::actor::ActorOwn network_manager; + td::actor::ActorOwn adnl; + td::actor::ActorOwn overlay_manager; + + td::actor::Scheduler scheduler({7}); + scheduler.run_in_context([&] { + ton::errorlog::ErrorLog::create(db_root_); + keyring = ton::keyring::Keyring::create(db_root_); + network_manager = td::actor::create_actor("test net"); + adnl = ton::adnl::Adnl::create(db_root_, keyring.get()); + overlay_manager = + ton::overlay::Overlays::create(db_root_, keyring.get(), adnl.get(), td::actor::ActorId{}); + td::actor::send_closure(adnl, &ton::adnl::Adnl::register_network_manager, network_manager.get()); + }); + + td::uint32 att = 0; + for (td::uint32 start = att; att < start + 5; att++) { + LOG(WARNING) << "Test #" << att; + root_nodes.resize(total_nodes); + slave_nodes.resize(total_nodes * node_slaves_cnt); + + auto overlay_id_full = + ton::create_serialize_tl_object(td::BufferSlice(PSTRING() << "TEST" << att)); + ton::overlay::OverlayIdFull overlay_id(overlay_id_full.clone()); + auto overlay_id_short = overlay_id.compute_short_id(); + + ton::overlay::OverlayOptions opts; + opts.max_slaves_in_semiprivate_overlay_ = node_slaves_cnt; + opts.default_permanent_members_flags_ = ton::overlay::OverlayMemberFlags::DoNotReceiveBroadcasts; + + ton::overlay::OverlayPrivacyRules rules( + 20 << 20, ton::overlay::CertificateFlags::AllowFec | ton::overlay::CertificateFlags::Trusted, {}); + + std::vector root_keys; + std::vector root_adnl; + + size_t real_members = 0; + + scheduler.run_in_context([&] { + auto addr = ton::adnl::TestLoopbackNetworkManager::generate_dummy_addr_list(); + + for (auto &n : root_nodes) { + bool receive_bcasts = (real_members == 0) ? true : (td::Random::fast_uint32() & 1); + if (receive_bcasts) { + real_members++; + } + n.can_receive = receive_bcasts; + + auto pk1 = ton::PrivateKey{ton::privkeys::Ed25519::random()}; + auto pub1 = pk1.compute_public_key(); + n.adnl_id_full = ton::adnl::AdnlNodeIdFull{pub1}; + n.adnl_id = ton::adnl::AdnlNodeIdShort{pub1.compute_short_id()}; + td::actor::send_closure(keyring, &ton::keyring::Keyring::add_key, std::move(pk1), true, [](td::Unit) {}); + td::actor::send_closure(adnl, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub1}, addr, + static_cast(0)); + td::actor::send_closure(network_manager, &ton::adnl::TestLoopbackNetworkManager::add_node_id, n.adnl_id, true, + true); + + auto pk2 = ton::PrivateKey{ton::privkeys::Ed25519::random()}; + auto pub2 = pk2.compute_public_key(); + n.id_full = pub2; + n.id = pub2.compute_short_id(); + n.pk = pk2; + td::actor::send_closure(keyring, &ton::keyring::Keyring::add_key, std::move(pk2), true, [](td::Unit) {}); + + LOG(DEBUG) << "created node " << n.adnl_id << " " << n.id; + + all_nodes.push_back(&n); + root_keys.push_back(n.id); + root_adnl.push_back(n.adnl_id); + } + + for (auto &n : slave_nodes) { + bool receive_bcasts = (real_members == 0) ? true : (td::Random::fast_uint32() & 1); + if (receive_bcasts) { + real_members++; + } + n.can_receive = receive_bcasts; + + auto pk1 = ton::PrivateKey{ton::privkeys::Ed25519::random()}; + auto pub1 = pk1.compute_public_key(); + n.adnl_id_full = ton::adnl::AdnlNodeIdFull{pub1}; + n.adnl_id = ton::adnl::AdnlNodeIdShort{pub1.compute_short_id()}; + td::actor::send_closure(keyring, &ton::keyring::Keyring::add_key, std::move(pk1), true, [](td::Unit) {}); + td::actor::send_closure(adnl, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub1}, addr, + static_cast(0)); + td::actor::send_closure(network_manager, &ton::adnl::TestLoopbackNetworkManager::add_node_id, n.adnl_id, true, + true); + + auto pk2 = ton::PrivateKey{ton::privkeys::Ed25519::random()}; + auto pub2 = pk2.compute_public_key(); + n.id_full = pub2; + n.id = pub2.compute_short_id(); + n.pk = pk2; + td::actor::send_closure(keyring, &ton::keyring::Keyring::add_key, std::move(pk2), true, [](td::Unit) {}); + + LOG(DEBUG) << "created node " << n.adnl_id << " " << n.id; + all_nodes.push_back(&n); + } + + for (auto &n1 : all_nodes) { + for (auto &n2 : all_nodes) { + td::actor::send_closure(adnl, &ton::adnl::Adnl::add_peer, n1->adnl_id, n2->adnl_id_full, addr); + } + } + + for (auto &n1 : root_nodes) { + opts.local_overlay_member_flags_ = + (n1.can_receive ? 0 : ton::overlay::OverlayMemberFlags::DoNotReceiveBroadcasts); + td::actor::send_closure(overlay_manager, &ton::overlay::Overlays::create_semiprivate_overlay, n1.adnl_id, + ton::overlay::OverlayIdFull(overlay_id_full.clone()), root_adnl, root_keys, + ton::overlay::OverlayMemberCertificate{}, std::make_unique(n1.can_receive), + rules, "", opts); + } + for (size_t i = 0; i < slave_nodes.size(); i++) { + auto &n1 = slave_nodes[i]; + opts.local_overlay_member_flags_ = + (n1.can_receive ? 0 : ton::overlay::OverlayMemberFlags::DoNotReceiveBroadcasts); + + ton::overlay::OverlayMemberCertificate cert(root_nodes[i / node_slaves_cnt].id_full, 0, i % node_slaves_cnt, + 2000000000, td::BufferSlice()); + auto buf = cert.to_sign_data(n1.adnl_id); + auto dec = root_nodes[i / node_slaves_cnt].pk.create_decryptor().move_as_ok(); + auto signature = dec->sign(buf.as_slice()).move_as_ok(); + cert.set_signature(signature.as_slice()); + auto enc = root_nodes[i / node_slaves_cnt].id_full.create_encryptor().move_as_ok(); + enc->check_signature(cert.to_sign_data(n1.adnl_id), cert.signature()).ensure(); + + td::actor::send_closure(overlay_manager, &ton::overlay::Overlays::create_semiprivate_overlay, n1.adnl_id, + ton::overlay::OverlayIdFull(overlay_id_full.clone()), root_adnl, root_keys, cert, + std::make_unique(n1.can_receive), rules, "", opts); + } + }); + + td::BufferSlice broadcast(1 << 20); + td::Random::secure_bytes(broadcast.as_slice()); + remaining = real_members; + bcast_hash = td::sha256_bits256(broadcast.as_slice()); + + auto t = td::Timestamp::in(20.0); + while (scheduler.run(1)) { + if (t.is_in_past()) { + break; + } + } + + scheduler.run_in_context([&] { + /*td::actor::send_closure(overlay_manager, &ton::overlay::Overlays::get_stats, + [&](td::Result> R) { + if (R.is_ok()) { + auto res = R.move_as_ok(); + for (auto &o : res->overlays_) { + if (o->overlay_id_ == overlay_id_short.bits256_value()) { + LOG(ERROR) << "NODE " << o->adnl_id_ << " nodes=" << o->nodes_.size(); + for (auto &x : o->stats_) { + LOG(ERROR) << "\t" << x->key_ << " " << x->value_; + } + for (auto &x : o->nodes_) { + LOG(ERROR) << "\t\t" << x->adnl_id_; + } + } + } + } + });*/ + td::actor::send_closure(overlay_manager, &ton::overlay::Overlays::send_broadcast_fec_ex, root_nodes[0].adnl_id, + overlay_id_short, root_nodes[0].id, 0, std::move(broadcast)); + }); + + t = td::Timestamp::in(10.0); + while (scheduler.run(1)) { + if (t.is_in_past()) { + break; + } + if (!remaining) { + break; + } + } + + LOG_CHECK(!remaining) << "remaining=" << remaining << " all=" << real_members; + + broadcast = td::BufferSlice(700); + td::Random::secure_bytes(broadcast.as_slice()); + remaining = real_members; + bcast_hash = td::sha256_bits256(broadcast.as_slice()); + scheduler.run_in_context([&] { + td::actor::send_closure(overlay_manager, &ton::overlay::Overlays::send_broadcast_ex, root_nodes[0].adnl_id, + overlay_id_short, root_nodes[0].id, 0, std::move(broadcast)); + }); + + t = td::Timestamp::in(10.0); + while (scheduler.run(1)) { + if (t.is_in_past()) { + break; + } + if (!remaining) { + break; + } + } + + LOG_CHECK(!remaining) << "remaining=" << remaining; + + scheduler.run_in_context([&] { + root_nodes.clear(); + slave_nodes.clear(); + all_nodes.clear(); + }); + } + + for (td::uint32 start = att; att < start + 5; att++) { + LOG(WARNING) << "Test #" << att; + root_nodes.resize(total_nodes); + + auto overlay_id_full = + ton::create_serialize_tl_object(td::BufferSlice(PSTRING() << "TEST" << att)); + ton::overlay::OverlayIdFull overlay_id(overlay_id_full.clone()); + auto overlay_id_short = overlay_id.compute_short_id(); + + ton::overlay::OverlayOptions opts; + + ton::overlay::OverlayPrivacyRules rules( + 20 << 20, ton::overlay::CertificateFlags::AllowFec | ton::overlay::CertificateFlags::Trusted, {}); + + std::vector root_keys; + std::vector root_adnl; + + size_t real_members = 0; + + scheduler.run_in_context([&] { + auto addr = ton::adnl::TestLoopbackNetworkManager::generate_dummy_addr_list(); + + for (auto &n : root_nodes) { + real_members++; + auto pk1 = ton::PrivateKey{ton::privkeys::Ed25519::random()}; + auto pub1 = pk1.compute_public_key(); + n.adnl_id_full = ton::adnl::AdnlNodeIdFull{pub1}; + n.adnl_id = ton::adnl::AdnlNodeIdShort{pub1.compute_short_id()}; + td::actor::send_closure(keyring, &ton::keyring::Keyring::add_key, std::move(pk1), true, [](td::Unit) {}); + td::actor::send_closure(adnl, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub1}, addr, + static_cast(0)); + td::actor::send_closure(network_manager, &ton::adnl::TestLoopbackNetworkManager::add_node_id, n.adnl_id, true, + true); + + auto pk2 = ton::PrivateKey{ton::privkeys::Ed25519::random()}; + auto pub2 = pk2.compute_public_key(); + n.id_full = pub2; + n.id = pub2.compute_short_id(); + n.pk = pk2; + td::actor::send_closure(keyring, &ton::keyring::Keyring::add_key, std::move(pk2), true, [](td::Unit) {}); + + LOG(DEBUG) << "created node " << n.adnl_id << " " << n.id; + + all_nodes.push_back(&n); + root_keys.push_back(n.id); + root_adnl.push_back(n.adnl_id); + } + + for (auto &n1 : all_nodes) { + for (auto &n2 : all_nodes) { + td::actor::send_closure(adnl, &ton::adnl::Adnl::add_peer, n1->adnl_id, n2->adnl_id_full, addr); + } + } + + for (auto &n1 : root_nodes) { + td::actor::send_closure(overlay_manager, &ton::overlay::Overlays::create_private_overlay_ex, n1.adnl_id, + ton::overlay::OverlayIdFull(overlay_id_full.clone()), root_adnl, + std::make_unique(true), rules, "", opts); + } + }); + + auto t = td::Timestamp::in(10.0); + while (scheduler.run(1)) { + if (t.is_in_past()) { + break; + } + } + + td::BufferSlice broadcast(1 << 20); + td::Random::secure_bytes(broadcast.as_slice()); + remaining = real_members; + bcast_hash = td::sha256_bits256(broadcast.as_slice()); + + scheduler.run_in_context([&] { + td::actor::send_closure(overlay_manager, &ton::overlay::Overlays::send_broadcast_fec_ex, root_nodes[0].adnl_id, + overlay_id_short, root_nodes[0].id, 0, std::move(broadcast)); + }); + + t = td::Timestamp::in(10.0); + while (scheduler.run(1)) { + if (t.is_in_past()) { + break; + } + if (!remaining) { + break; + } + } + + LOG_CHECK(!remaining) << "remaining=" << remaining; + + broadcast = td::BufferSlice(700); + td::Random::secure_bytes(broadcast.as_slice()); + remaining = real_members; + bcast_hash = td::sha256_bits256(broadcast.as_slice()); + scheduler.run_in_context([&] { + td::actor::send_closure(overlay_manager, &ton::overlay::Overlays::send_broadcast_ex, root_nodes[0].adnl_id, + overlay_id_short, root_nodes[0].id, 0, std::move(broadcast)); + }); + + t = td::Timestamp::in(10.0); + while (scheduler.run(1)) { + if (t.is_in_past()) { + break; + } + if (!remaining) { + break; + } + } + + LOG_CHECK(!remaining) << "remaining=" << remaining; + + scheduler.run_in_context([&] { + root_nodes.clear(); + slave_nodes.clear(); + all_nodes.clear(); + }); + } + + td::rmrf(db_root_).ensure(); + std::_Exit(0); + return 0; +} diff --git a/test/test-ton-collator.cpp b/test/test-ton-collator.cpp index 60476cf8f..7c13870e5 100644 --- a/test/test-ton-collator.cpp +++ b/test/test-ton-collator.cpp @@ -350,7 +350,7 @@ class TestNode : public td::actor::Actor { void send_block_candidate(ton::BlockIdExt block_id, ton::CatchainSeqno cc_seqno, td::uint32 validator_set_hash, td::BufferSlice data) override { } - void send_broadcast(ton::BlockBroadcast broadcast, bool custom_overlays_only) override { + void send_broadcast(ton::BlockBroadcast broadcast, int mode) override { } void download_block(ton::BlockIdExt block_id, td::uint32 priority, td::Timestamp timeout, td::Promise promise) override { diff --git a/test/test-validator-session-state.cpp b/test/test-validator-session-state.cpp index cb5d817f2..a675070ce 100644 --- a/test/test-validator-session-state.cpp +++ b/test/test-validator-session-state.cpp @@ -101,6 +101,10 @@ class Description : public ton::validatorsession::ValidatorSessionDescription { td::uint32 get_max_priority() const override { return opts_.round_candidates - 1; } + td::uint32 get_node_by_priority(td::uint32 round, td::uint32 priority) const override { + CHECK(priority <= get_max_priority()); + return (round + priority) % get_total_nodes(); + } td::uint32 get_unixtime(td::uint64 ts) const override { return static_cast(ts >> 32); } diff --git a/tl-utils/common-utils.hpp b/tl-utils/common-utils.hpp index d61bd79c0..05f8a825f 100644 --- a/tl-utils/common-utils.hpp +++ b/tl-utils/common-utils.hpp @@ -148,6 +148,39 @@ td::Result::value, T>>> } } +template +td::Result::value, T>>> fetch_tl_prefix(td::Slice &data, + bool boxed) { + td::TlParser p(data); + tl_object_ptr R; + if (boxed) { + R = TlFetchBoxed, T::ID>::parse(p); + } else { + R = move_tl_object_as(T::fetch(p)); + } + if (p.get_status().is_ok()) { + data.remove_prefix(data.size() - p.get_left_len()); + return std::move(R); + } else { + return p.get_status(); + } +} + +template +td::Result::value, T>>> fetch_tl_prefix(td::Slice &data, + bool boxed) { + CHECK(boxed); + td::TlParser p(data); + tl_object_ptr R; + R = move_tl_object_as(T::fetch(p)); + if (p.get_status().is_ok()) { + data.remove_prefix(data.size() - p.get_left_len()); + return std::move(R); + } else { + return p.get_status(); + } +} + template [[deprecated]] tl_object_ptr clone_tl_object(const tl_object_ptr &obj) { auto B = serialize_tl_object(obj, true); diff --git a/tl-utils/lite-utils.cpp b/tl-utils/lite-utils.cpp index daa3dbaf0..387474cfa 100644 --- a/tl-utils/lite-utils.cpp +++ b/tl-utils/lite-utils.cpp @@ -160,6 +160,8 @@ std::string lite_query_name_by_id(int id) { {lite_api::liteServer_getShardBlockProof::ID, "getShardBlockProof"}, {lite_api::liteServer_getOutMsgQueueSizes::ID, "getOutMsgQueueSizes"}, {lite_api::liteServer_getBlockOutMsgQueueSize::ID, "getBlockOutMsgQueueSize"}, + {lite_api::liteServer_getDispatchQueueInfo::ID, "getDispatchQueueInfo"}, + {lite_api::liteServer_getDispatchQueueMessages::ID, "getDispatchQueueMessages"}, {lite_api::liteServer_nonfinal_getCandidate::ID, "nonfinal.getCandidate"}, {lite_api::liteServer_nonfinal_getValidatorGroups::ID, "nonfinal.getValidatorGroups"}}; auto it = names.find(id); diff --git a/tl/generate/scheme/lite_api.tl b/tl/generate/scheme/lite_api.tl index 879d7ff4a..b00951da4 100644 --- a/tl/generate/scheme/lite_api.tl +++ b/tl/generate/scheme/lite_api.tl @@ -61,6 +61,11 @@ liteServer.lookupBlockResult id:tonNode.blockIdExt mode:# mc_block_id:tonNode.bl liteServer.outMsgQueueSize id:tonNode.blockIdExt size:int = liteServer.OutMsgQueueSize; liteServer.outMsgQueueSizes shards:(vector liteServer.outMsgQueueSize) ext_msg_queue_size_limit:int = liteServer.OutMsgQueueSizes; liteServer.blockOutMsgQueueSize mode:# id:tonNode.blockIdExt size:long proof:mode.0?bytes = liteServer.BlockOutMsgQueueSize; +liteServer.accountDispatchQueueInfo addr:int256 size:long min_lt:long max_lt:long = liteServer.AccountDispatchQueueInfo; +liteServer.dispatchQueueInfo mode:# id:tonNode.blockIdExt account_dispatch_queues:(vector liteServer.accountDispatchQueueInfo) complete:Bool proof:mode.0?bytes = liteServer.DispatchQueueInfo; +liteServer.dispatchQueueMessage addr:int256 lt:long hash:int256 metadata:liteServer.transactionMetadata = liteServer.DispatchQueueMessage; +liteServer.dispatchQueueMessages mode:# id:tonNode.blockIdExt messages:(vector liteServer.dispatchQueueMessage) complete:Bool + proof:mode.0?bytes messages_boc:mode.2?bytes = liteServer.DispatchQueueMessages; liteServer.debug.verbosity value:int = liteServer.debug.Verbosity; @@ -100,6 +105,9 @@ liteServer.getLibrariesWithProof id:tonNode.blockIdExt mode:# library_list:(vect liteServer.getShardBlockProof id:tonNode.blockIdExt = liteServer.ShardBlockProof; liteServer.getOutMsgQueueSizes mode:# wc:mode.0?int shard:mode.0?long = liteServer.OutMsgQueueSizes; liteServer.getBlockOutMsgQueueSize mode:# id:tonNode.blockIdExt want_proof:mode.0?true = liteServer.BlockOutMsgQueueSize; +liteServer.getDispatchQueueInfo mode:# id:tonNode.blockIdExt after_addr:mode.1?int256 max_accounts:int want_proof:mode.0?true = liteServer.DispatchQueueInfo; +liteServer.getDispatchQueueMessages mode:# id:tonNode.blockIdExt addr:int256 after_lt:long max_messages:int + want_proof:mode.0?true one_account:mode.1?true messages_boc:mode.2?true = liteServer.DispatchQueueMessages; liteServer.nonfinal.getValidatorGroups mode:# wc:mode.0?int shard:mode.0?long = liteServer.nonfinal.ValidatorGroups; liteServer.nonfinal.getCandidate id:liteServer.nonfinal.candidateId = liteServer.nonfinal.Candidate; diff --git a/tl/generate/scheme/lite_api.tlo b/tl/generate/scheme/lite_api.tlo index 6ece1d20f..c576a7f45 100644 Binary files a/tl/generate/scheme/lite_api.tlo and b/tl/generate/scheme/lite_api.tlo differ diff --git a/tl/generate/scheme/ton_api.tl b/tl/generate/scheme/ton_api.tl index 10b1a9762..9ef47d95e 100644 --- a/tl/generate/scheme/ton_api.tl +++ b/tl/generate/scheme/ton_api.tl @@ -228,10 +228,15 @@ dht.query node:dht.node = True; ---types--- overlay.node.toSign id:adnl.id.short overlay:int256 version:int = overlay.node.ToSign; +overlay.node.toSignEx id:adnl.id.short overlay:int256 flags:int version:int = overlay.node.ToSign; overlay.node id:PublicKey overlay:int256 version:int signature:bytes = overlay.Node; +overlay.nodeV2 id:PublicKey overlay:int256 flags:int version:int signature:bytes certificate:overlay.MemberCertificate = overlay.NodeV2; overlay.nodes nodes:(vector overlay.node) = overlay.Nodes; +overlay.nodesV2 nodes:(vector overlay.NodeV2) = overlay.NodesV2; +overlay.messageExtra flags:# certificate:flags.0?overlay.MemberCertificate = overlay.MessageExtra; overlay.message overlay:int256 = overlay.Message; +overlay.messageWithExtra overlay:int256 extra:overlay.messageExtra = overlay.Message; //overlay.randomPeers peers:(vector adnl.node) = overlay.RandomPeers; overlay.broadcastList hashes:(vector int256) = overlay.BroadcastList; @@ -244,6 +249,9 @@ overlay.broadcastFec.partId broadcast_hash:int256 data_hash:int256 seqno:int = o overlay.broadcast.toSign hash:int256 date:int = overlay.broadcast.ToSign; +overlay.memberCertificateId node:adnl.id.short flags:int slot:int expire_at:int = overlay.MemberCertificateId; +overlay.memberCertificate issued_by:PublicKey flags:int slot:int expire_at:int signature:bytes = overlay.MemberCertificate; +overlay.emptyMemberCertificate = overlay.MemberCertificate; overlay.certificate issued_by:PublicKey expire_at:int max_size:int signature:bytes = overlay.Certificate; overlay.certificateV2 issued_by:PublicKey expire_at:int max_size:int flags:int signature:bytes = overlay.Certificate; overlay.emptyCertificate = overlay.Certificate; @@ -261,13 +269,16 @@ overlay.broadcastNotFound = overlay.Broadcast; ---functions--- overlay.getRandomPeers peers:overlay.nodes = overlay.Nodes; +overlay.getRandomPeersV2 peers:overlay.NodesV2 = overlay.NodesV2; overlay.query overlay:int256 = True; +overlay.queryWithExtra overlay:int256 extra:overlay.messageExtra = True; overlay.getBroadcast hash:int256 = overlay.Broadcast; overlay.getBroadcastList list:overlay.broadcastList = overlay.BroadcastList; ---types--- +overlay.db.nodesV2 nodes:overlay.nodesV2 = overlay.db.Nodes; overlay.db.nodes nodes:overlay.nodes = overlay.db.Nodes; overlay.db.key.nodes local_id:int256 overlay:int256 = overlay.db.Key; @@ -385,6 +396,7 @@ tonNode.blockSignature who:int256 signature:bytes = tonNode.BlockSignature; tonNode.blockId workchain:int shard:long seqno:int = tonNode.BlockId; tonNode.blockIdExt workchain:int shard:long seqno:int root_hash:int256 file_hash:int256 = tonNode.BlockIdExt; tonNode.zeroStateIdExt workchain:int root_hash:int256 file_hash:int256 = tonNode.ZeroStateIdExt; +tonNode.shardId workchain:int shard:long = tonNode.ShardId; tonNode.blockDescriptionEmpty = tonNode.BlockDescription; tonNode.blockDescription id:tonNode.blockIdExt = tonNode.BlockDescription; @@ -521,6 +533,7 @@ db.filedb.key.proof block_id:tonNode.blockIdExt = db.filedb.Key; db.filedb.key.proofLink block_id:tonNode.blockIdExt = db.filedb.Key; db.filedb.key.signatures block_id:tonNode.blockIdExt = db.filedb.Key; db.filedb.key.candidate id:db.candidate.id = db.filedb.Key; +db.filedb.key.candidateRef id:tonNode.blockIdExt = db.filedb.Key; db.filedb.key.blockInfo block_id:tonNode.blockIdExt = db.filedb.Key; db.filedb.value key:db.filedb.Key prev:int256 next:int256 file_hash:int256 = db.filedb.Value; @@ -655,6 +668,7 @@ engine.validator.signature signature:bytes = engine.validator.Signature; engine.validator.oneStat key:string value:string = engine.validator.OneStat; engine.validator.stats stats:(vector engine.validator.oneStat) = engine.validator.Stats; +engine.validator.textStats data:string = engine.validator.TextStats; engine.validator.controlQueryError code:int message:string = engine.validator.ControlQueryError; @@ -669,9 +683,16 @@ engine.validator.proposalVote perm_key:int256 to_send:bytes = engine.validator.P engine.validator.dhtServerStatus id:int256 status:int = engine.validator.DhtServerStatus; engine.validator.dhtServersStatus servers:(vector engine.validator.dhtServerStatus) = engine.validator.DhtServersStatus; -engine.validator.overlayStatsNode adnl_id:int256 ip_addr:string bdcst_errors:int fec_bdcst_errors:int last_in_query:int last_out_query:int t_out_bytes:int t_in_bytes:int t_out_pckts:int t_in_pckts:int = engine.validator.OverlayStatsNode; +engine.validator.overlayStatsTraffic t_out_bytes:long t_in_bytes:long t_out_pckts:int t_in_pckts:int = engine.validator.OverlayStatsTraffic; -engine.validator.overlayStats overlay_id:int256 overlay_id_full:PublicKey adnl_id:int256 scope:string nodes:(vector engine.validator.overlayStatsNode) stats:(vector engine.validator.oneStat) = engine.validator.OverlayStats; +engine.validator.overlayStatsNode adnl_id:int256 ip_addr:string is_neighbour:Bool is_alive:Bool node_flags:int + bdcst_errors:int fec_bdcst_errors:int last_in_query:int last_out_query:int + traffic:engine.validator.overlayStatsTraffic traffic_responses:engine.validator.overlayStatsTraffic = engine.validator.OverlayStatsNode; + +engine.validator.overlayStats overlay_id:int256 overlay_id_full:PublicKey adnl_id:int256 scope:string + nodes:(vector engine.validator.overlayStatsNode) stats:(vector engine.validator.oneStat) + total_traffic:engine.validator.overlayStatsTraffic total_traffic_responses:engine.validator.overlayStatsTraffic + extra:string = engine.validator.OverlayStats; engine.validator.overlaysStats overlays:(vector engine.validator.overlayStats) = engine.validator.OverlaysStats; engine.validator.onePerfTimerStat time:int min:double avg:double max:double = engine.validator.OnePerfTimerStat; @@ -743,7 +764,8 @@ engine.validator.setStateSerializerEnabled enabled:Bool = engine.validator.Succe engine.validator.setCollatorOptionsJson json:string = engine.validator.Success; engine.validator.getCollatorOptionsJson = engine.validator.JsonConfig; -engine.validator.getAdnlStats = adnl.Stats; +engine.validator.getAdnlStats all:Bool = adnl.Stats; +engine.validator.getActorTextStats = engine.validator.TextStats; ---types--- diff --git a/tl/generate/scheme/ton_api.tlo b/tl/generate/scheme/ton_api.tlo index 7ee378c1a..bc46d1b16 100644 Binary files a/tl/generate/scheme/ton_api.tlo and b/tl/generate/scheme/ton_api.tlo differ diff --git a/tl/generate/scheme/tonlib_api.tl b/tl/generate/scheme/tonlib_api.tl index 6cf40d005..5110d6ecd 100644 --- a/tl/generate/scheme/tonlib_api.tl +++ b/tl/generate/scheme/tonlib_api.tl @@ -50,8 +50,10 @@ internal.transactionId lt:int64 hash:bytes = internal.TransactionId; ton.blockId workchain:int32 shard:int64 seqno:int32 = internal.BlockId; ton.blockIdExt workchain:int32 shard:int64 seqno:int32 root_hash:bytes file_hash:bytes = ton.BlockIdExt; -raw.fullAccountState balance:int64 code:bytes data:bytes last_transaction_id:internal.transactionId block_id:ton.blockIdExt frozen_hash:bytes sync_utime:int53 = raw.FullAccountState; -raw.message source:accountAddress destination:accountAddress value:int64 fwd_fee:int64 ihr_fee:int64 created_lt:int64 body_hash:bytes msg_data:msg.Data = raw.Message; +extraCurrency id:int32 amount:int64 = ExtraCurrency; + +raw.fullAccountState balance:int64 extra_currencies:vector code:bytes data:bytes last_transaction_id:internal.transactionId block_id:ton.blockIdExt frozen_hash:bytes sync_utime:int53 = raw.FullAccountState; +raw.message source:accountAddress destination:accountAddress value:int64 extra_currencies:vector fwd_fee:int64 ihr_fee:int64 created_lt:int64 body_hash:bytes msg_data:msg.Data = raw.Message; raw.transaction address:accountAddress utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 storage_fee:int64 other_fee:int64 in_msg:raw.message out_msgs:vector = raw.Transaction; raw.transactions transactions:vector previous_transaction_id:internal.transactionId = raw.Transactions; @@ -87,7 +89,7 @@ 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; -fullAccountState address:accountAddress balance:int64 last_transaction_id:internal.transactionId block_id:ton.blockIdExt sync_utime:int53 account_state:AccountState revision:int32 = FullAccountState; +fullAccountState address:accountAddress balance:int64 extra_currencies:vector last_transaction_id:internal.transactionId block_id:ton.blockIdExt sync_utime:int53 account_state:AccountState revision:int32 = FullAccountState; accountRevisionList revisions:vector = AccountRevisionList; accountList accounts:vector = AccountList; @@ -110,7 +112,7 @@ msg.dataDecrypted proof:bytes data:msg.Data = msg.DataDecrypted; msg.dataEncryptedArray elements:vector = msg.DataEncryptedArray; msg.dataDecryptedArray elements:vector = msg.DataDecryptedArray; -msg.message destination:accountAddress public_key:string amount:int64 data:msg.Data send_mode:int32 = msg.Message; +msg.message destination:accountAddress public_key:string amount:int64 extra_currencies:vector data:msg.Data send_mode:int32 = msg.Message; // // DNS diff --git a/tl/generate/scheme/tonlib_api.tlo b/tl/generate/scheme/tonlib_api.tlo index 686bd9181..70c08459c 100644 Binary files a/tl/generate/scheme/tonlib_api.tlo and b/tl/generate/scheme/tonlib_api.tlo differ diff --git a/ton/ton-tl.hpp b/ton/ton-tl.hpp index 6d676ea8d..1bc9a28c8 100644 --- a/ton/ton-tl.hpp +++ b/ton/ton-tl.hpp @@ -19,7 +19,8 @@ #pragma once #include "ton-types.h" -#include "auto/tl/ton_api.h" +#include "auto/tl/ton_api.hpp" +#include "td/utils/overloaded.h" namespace ton { @@ -53,4 +54,12 @@ inline ZeroStateIdExt create_zero_state_id(tl_object_ptrworkchain_, B->root_hash_, B->file_hash_}; } +inline ShardIdFull create_shard_id(const tl_object_ptr &s) { + return ShardIdFull{s->workchain_, static_cast(s->shard_)}; +} + +inline tl_object_ptr create_tl_shard_id(const ShardIdFull &s) { + return create_tl_object(s.workchain, s.shard); +} + } // namespace ton diff --git a/ton/ton-types.h b/ton/ton-types.h index 915682655..efdb795d1 100644 --- a/ton/ton-types.h +++ b/ton/ton-types.h @@ -51,6 +51,8 @@ using ValidatorSessionId = td::Bits256; constexpr WorkchainId masterchainId = -1, basechainId = 0, workchainInvalid = 0x80000000; constexpr ShardId shardIdAll = (1ULL << 63); +constexpr int max_shard_pfx_len = 60; + enum GlobalCapabilities { capIhrEnabled = 1, capCreateStatsEnabled = 2, diff --git a/tonlib/CMakeLists.txt b/tonlib/CMakeLists.txt index dc3e90300..67d18e019 100644 --- a/tonlib/CMakeLists.txt +++ b/tonlib/CMakeLists.txt @@ -62,7 +62,7 @@ target_include_directories(tonlib PUBLIC $/.. $ ) -target_link_libraries(tonlib PRIVATE tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ton_crypto_core ton_block lite-client-common smc-envelope emulator_static) +target_link_libraries(tonlib PRIVATE tdactor adnllite tl_lite_api tl-lite-utils ton_crypto lite-client-common smc-envelope emulator_static) target_link_libraries(tonlib PUBLIC tdutils tl_tonlib_api) if (TONLIB_ENABLE_JNI AND NOT ANDROID) # jni is available by default on Android diff --git a/tonlib/test/online.cpp b/tonlib/test/online.cpp index 52f517200..cf892c292 100644 --- a/tonlib/test/online.cpp +++ b/tonlib/test/online.cpp @@ -264,7 +264,8 @@ td::Result create_send_grams_query(Client& client, const Wallet& source data = tonlib_api::make_object(message.raw.unwrap(), message.init_state.unwrap()); } msgs.push_back(tonlib_api::make_object( - tonlib_api::make_object(destination), "", amount, std::move(data), -1)); + tonlib_api::make_object(destination), "", amount, + std::vector>{}, std::move(data), -1)); auto r_id = sync_send(client, tonlib_api::make_object( @@ -566,7 +567,7 @@ void test_multisig(Client& client, const Wallet& giver_wallet) { for (int i = 0; i < 2; i++) { // Just transfer all (some) money back in one query vm::CellBuilder icb; - ton::GenericAccount::store_int_message(icb, block::StdAddress::parse(giver_wallet.address).move_as_ok(), 1); + ton::GenericAccount::store_int_message(icb, block::StdAddress::parse(giver_wallet.address).move_as_ok(), 1, {}); icb.store_bytes("\0\0\0\0", 4); vm::CellString::store(icb, "Greatings from multisig", 35 * 8).ensure(); ton::MultisigWallet::QueryBuilder qb(wallet_id, -1 - i, icb.finalize()); diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index f62b9ae47..7b2238399 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -178,6 +178,7 @@ static block::AccountState create_account_state(ton::tl_object_ptr extra_currencies; ton::UnixTime storage_last_paid{0}; vm::CellStorageStat storage_stat; @@ -206,6 +207,74 @@ std::string to_bytes(td::Ref cell) { return vm::std_boc_serialize(cell, vm::BagOfCells::Mode::WithCRC32C).move_as_ok().as_slice().str(); } +td::Result>> parse_extra_currencies_or_throw( + const td::Ref dict_root) { + std::vector> result; + vm::Dictionary dict{dict_root, 32}; + if (!dict.check_for_each([&](td::Ref value, td::ConstBitPtr key, int n) { + CHECK(n == 32); + int id = (int)key.get_int(n); + auto amount_ref = block::tlb::t_VarUIntegerPos_32.as_integer_skip(value.write()); + if (amount_ref.is_null() || !value->empty_ext()) { + return false; + } + td::int64 amount = amount_ref->to_long(); + if (amount == td::int64(~0ULL << 63)) { + return false; + } + result.push_back(tonlib_api::make_object(id, amount)); + return true; + })) { + return td::Status::Error("Failed to parse extra currencies dict"); + } + return result; +} + +td::Result>> parse_extra_currencies( + const td::Ref& dict_root) { + return TRY_VM(parse_extra_currencies_or_throw(dict_root)); +} + +td::Result> to_extra_currenctes_dict( + const std::vector>& extra_currencies) { + vm::Dictionary dict{32}; + for (const auto &f : extra_currencies) { + if (f->amount_ == 0) { + continue; + } + if (f->amount_ < 0) { + return td::Status::Error("Negative extra currency amount"); + } + vm::CellBuilder cb2; + block::tlb::t_VarUInteger_32.store_integer_value(cb2, *td::make_refint(f->amount_)); + if (!dict.set_builder(td::BitArray<32>(f->id_), cb2, vm::DictionaryBase::SetMode::Add)) { + return td::Status::Error("Duplicate extra currency id"); + } + } + return std::move(dict).extract_root_cell(); +} + +td::Status check_enough_extra_currencies(const td::Ref &balance, const td::Ref &amount) { + block::CurrencyCollection c1{td::zero_refint(), balance}; + block::CurrencyCollection c2{td::zero_refint(), amount}; + auto res = TRY_VM(td::Result{c1 >= c2}); + TRY_RESULT(v, std::move(res)); + if (!v) { + return TonlibError::NotEnoughFunds(); + } + return td::Status::OK(); +} + +td::Result> add_extra_currencies(const td::Ref &e1, const td::Ref &e2) { + block::CurrencyCollection c1{td::zero_refint(), e1}; + block::CurrencyCollection c2{td::zero_refint(), e2}; + TRY_RESULT_ASSIGN(c1, TRY_VM(td::Result{c1 + c2})); + if (c1.is_valid()) { + return td::Status::Error("Failed to add extra currencies"); + } + return c1.extra; +} + td::Result get_public_key(td::Slice public_key) { TRY_RESULT_PREFIX(address, block::PublicKey::parse(public_key), TonlibError::InvalidPublicKey()); return address; @@ -313,9 +382,10 @@ class AccountState { if (state.data.not_null()) { data = to_bytes(state.data); } + TRY_RESULT(extra_currencies, parse_extra_currencies(get_extra_currencies())); return tonlib_api::make_object( - get_balance(), std::move(code), std::move(data), to_transaction_id(raw().info), to_tonlib_api(raw().block_id), - raw().frozen_hash, get_sync_time()); + get_balance(), std::move(extra_currencies), std::move(code), std::move(data), to_transaction_id(raw().info), + to_tonlib_api(raw().block_id), raw().frozen_hash, get_sync_time()); } td::Result> to_wallet_v3_accountState() const { @@ -447,10 +517,11 @@ class AccountState { td::Result> to_fullAccountState() const { TRY_RESULT(account_state, to_accountState()); + TRY_RESULT(extra_currencies, parse_extra_currencies(get_extra_currencies())); return tonlib_api::make_object( tonlib_api::make_object(get_address().rserialize(true)), get_balance(), - to_transaction_id(raw().info), to_tonlib_api(raw().block_id), get_sync_time(), std::move(account_state), - get_wallet_revision()); + std::move(extra_currencies), to_transaction_id(raw().info), to_tonlib_api(raw().block_id), get_sync_time(), + std::move(account_state), get_wallet_revision()); } td::Result> to_shardAccountCell() const { @@ -550,6 +621,10 @@ class AccountState { return raw_.balance; } + td::Ref get_extra_currencies() const { + return raw_.extra_currencies; + } + const RawAccountState& raw() const { return raw_; } @@ -1342,6 +1417,7 @@ class GetRawAccountState : public td::actor::Actor { } TRY_RESULT(balance, to_balance(storage.balance)); res.balance = balance; + res.extra_currencies = storage.balance->prefetch_ref(); auto state_tag = block::gen::t_AccountState.get_tag(*storage.state); if (state_tag < 0) { return td::Status::Error("Failed to parse AccountState tag"); @@ -1768,7 +1844,7 @@ class RunEmulator : public TonlibQueryActor { void get_block_id(td::Promise&& promise) { auto shard_id = ton::shard_prefix(request_.address.addr, 60); auto query = ton::lite_api::liteServer_lookupBlock(0b111111010, ton::create_tl_lite_block_id_simple({request_.address.workchain, shard_id, 0}), request_.lt, 0); - client_.send_query(std::move(query), promise.wrap([self = this, shard_id](td::Result> header_r) -> td::Result { + client_.send_query(std::move(query), promise.wrap([shard_id](td::Result> header_r) -> td::Result { TRY_RESULT(header, std::move(header_r)); ton::BlockIdExt block_id = ton::create_block_id(header->id_); @@ -1817,7 +1893,7 @@ class RunEmulator : public TonlibQueryActor { void get_mc_state_root(td::Promise>&& promise) { TRY_RESULT_PROMISE(promise, lite_block, to_lite_api(*to_tonlib_api(block_id_.mc))); auto block = ton::create_block_id(lite_block); - client_.send_query(ton::lite_api::liteServer_getConfigAll(0b11'11111111, std::move(lite_block)), promise.wrap([self = this, block](auto r_config) -> td::Result> { + client_.send_query(ton::lite_api::liteServer_getConfigAll(0b11'11111111, std::move(lite_block)), promise.wrap([block](auto r_config) -> td::Result> { TRY_RESULT(state, block::check_extract_state_proof(block, r_config->state_proof_.as_slice(), r_config->config_proof_.as_slice())); @@ -1842,7 +1918,7 @@ class RunEmulator : public TonlibQueryActor { constexpr int req_count = 256; auto query = ton::lite_api::liteServer_listBlockTransactions(std::move(lite_block), mode, req_count, std::move(after), false, true); - client_.send_query(std::move(query), [self = this, mode, lt, root_hash = block_id_.id.root_hash, req_count](lite_api_ptr&& bTxes) { + client_.send_query(std::move(query), [self = this, mode, lt, root_hash = block_id_.id.root_hash](lite_api_ptr&& bTxes) { if (!bTxes) { self->check(td::Status::Error("liteServer.blockTransactions is null")); return; @@ -2008,7 +2084,9 @@ class RunEmulator : public TonlibQueryActor { account = std::move(emulation_result.move_as_ok().account); RawAccountState raw = std::move(account_state_->raw()); raw.block_id = block_id_.id; - raw.balance = account.get_balance().grams->to_long(); + block::CurrencyCollection balance = account.get_balance(); + raw.balance = balance.grams->to_long(); + raw.extra_currencies = balance.extra; raw.storage_last_paid = std::move(account.last_paid); raw.storage_stat = std::move(account.storage_stat); raw.code = std::move(account.code); @@ -3029,6 +3107,7 @@ struct ToRawTransactions { } TRY_RESULT(balance, to_balance(msg_info.value)); + TRY_RESULT(extra_currencies, parse_extra_currencies(msg_info.value->prefetch_ref())); TRY_RESULT(src, to_std_address(msg_info.src)); TRY_RESULT(dest, to_std_address(msg_info.dest)); TRY_RESULT(fwd_fee, to_balance(msg_info.fwd_fee)); @@ -3037,8 +3116,9 @@ struct ToRawTransactions { return tonlib_api::make_object( tonlib_api::make_object(src), - tonlib_api::make_object(std::move(dest)), balance, fwd_fee, ihr_fee, created_lt, - std::move(body_hash), get_data(src)); + tonlib_api::make_object(std::move(dest)), balance, + std::move(extra_currencies), fwd_fee, ihr_fee, created_lt, std::move(body_hash), + get_data(src)); } case block::gen::CommonMsgInfo::ext_in_msg_info: { block::gen::CommonMsgInfo::Record_ext_in_msg_info msg_info; @@ -3048,7 +3128,8 @@ struct ToRawTransactions { TRY_RESULT(dest, to_std_address(msg_info.dest)); return tonlib_api::make_object( tonlib_api::make_object(), - tonlib_api::make_object(std::move(dest)), 0, 0, 0, 0, std::move(body_hash), + tonlib_api::make_object(std::move(dest)), 0, + std::vector>{}, 0, 0, 0, std::move(body_hash), get_data("")); } case block::gen::CommonMsgInfo::ext_out_msg_info: { @@ -3060,7 +3141,9 @@ struct ToRawTransactions { auto created_lt = static_cast(msg_info.created_lt); return tonlib_api::make_object( tonlib_api::make_object(src), - tonlib_api::make_object(), 0, 0, 0, created_lt, std::move(body_hash), get_data(src)); + tonlib_api::make_object(), 0, + std::vector>{}, 0, 0, created_lt, std::move(body_hash), + get_data(src)); } } @@ -3516,6 +3599,7 @@ class GenericCreateSendGrams : public TonlibQueryActor { struct Action { block::StdAddress destination; td::int64 amount; + td::Ref extra_currencies; td::int32 send_mode{-1}; bool is_encrypted{false}; @@ -3563,6 +3647,7 @@ class GenericCreateSendGrams : public TonlibQueryActor { return TonlibError::InvalidField("amount", "can't be negative"); } res.amount = message.amount_; + TRY_RESULT_ASSIGN(res.extra_currencies, to_extra_currenctes_dict(message.extra_currencies_)); if (!message.public_key_.empty()) { TRY_RESULT(public_key, get_public_key(message.public_key_)); auto key = td::Ed25519::PublicKey(td::SecureString(public_key.key)); @@ -3973,8 +4058,10 @@ class GenericCreateSendGrams : public TonlibQueryActor { } td::int64 amount = 0; + td::Ref extra_currencies; for (auto& action : actions_) { amount += action.amount; + TRY_RESULT_ASSIGN(extra_currencies, add_extra_currencies(extra_currencies, action.extra_currencies)); } if (amount > source_->get_balance()) { @@ -3986,6 +4073,8 @@ class GenericCreateSendGrams : public TonlibQueryActor { return TonlibError::NotEnoughFunds(); } + TRY_STATUS(check_enough_extra_currencies(source_->get_extra_currencies(), extra_currencies)); + if (source_->get_wallet_type() == AccountState::RestrictedWallet) { auto r_unlocked_balance = ton::RestrictedWallet::create(source_->get_smc_state()) ->get_balance(source_->get_balance(), source_->get_sync_time()); @@ -4003,12 +4092,13 @@ class GenericCreateSendGrams : public TonlibQueryActor { auto& destination = destinations_[i]; gift.destination = destinations_[i]->get_address(); gift.gramms = action.amount; + gift.extra_currencies = action.extra_currencies; gift.send_mode = action.send_mode; // Temporary turn off this dangerous transfer - if (false && action.amount == source_->get_balance()) { - gift.gramms = -1; - } + // if (action.amount == source_->get_balance()) { + // gift.gramms = -1; + // } if (action.body.not_null()) { gift.body = action.body; @@ -5867,7 +5957,7 @@ td::Status TonlibClient::do_request(const tonlib_api::blocks_getTransactions& re std::move(after), reverse_mode, check_proof), - promise.wrap([check_proof, reverse_mode, root_hash, req_count = request.count_, start_addr, start_lt, mode = request.mode_] + promise.wrap([root_hash, req_count = request.count_, start_addr, start_lt, mode = request.mode_] (lite_api_ptr&& bTxes) -> td::Result> { TRY_STATUS(check_block_transactions_proof(bTxes, mode, start_lt, start_addr, root_hash, req_count)); diff --git a/tonlib/tonlib/tonlib-cli.cpp b/tonlib/tonlib/tonlib-cli.cpp index f955adb5a..4567478eb 100644 --- a/tonlib/tonlib/tonlib-cli.cpp +++ b/tonlib/tonlib/tonlib-cli.cpp @@ -1108,9 +1108,9 @@ class TonlibCli : public td::actor::Actor { void pchan_init_2(Address addr, td::int32 pchan_id, td::int64 value, tonlib_api::object_ptr query, td::Promise promise) { std::vector> messages; - messages.push_back( - make_object(channels_[pchan_id].to_address(), "", value, - make_object(query->body_, query->init_state_), -1)); + messages.push_back(make_object( + channels_[pchan_id].to_address(), "", value, std::vector>{}, + make_object(query->body_, query->init_state_), -1)); auto action = make_object(std::move(messages), true); send_query( make_object(addr.input_key(), std::move(addr.address), 60, std::move(action), nullptr), @@ -2058,6 +2058,19 @@ class TonlibCli : public td::actor::Actor { }); } + static void print_full_account_state(const ton::tl_object_ptr& state) { + td::StringBuilder balance_str; + balance_str << "Balance: " << Grams{td::narrow_cast(state->balance_ * (state->balance_ > 0))}; + for (const auto& extra : state->extra_currencies_) { + balance_str << " + " << extra->amount_ << ".$" << extra->id_; + } + td::TerminalIO::out() << balance_str.as_cslice() << "\n"; + td::TerminalIO::out() << "Sync utime: " << state->sync_utime_ << "\n"; + td::TerminalIO::out() << "transaction.LT: " << state->last_transaction_id_->lt_ << "\n"; + td::TerminalIO::out() << "transaction.Hash: " << td::base64_encode(state->last_transaction_id_->hash_) << "\n"; + td::TerminalIO::out() << to_string(state->account_state_); + } + void get_state(td::Slice key, td::Promise promise) { TRY_RESULT_PROMISE(promise, address, to_account_address(key, false)); @@ -2066,14 +2079,7 @@ class TonlibCli : public td::actor::Actor { ton::move_tl_object_as(std::move(address.address))), promise.wrap([address_str](auto&& state) { td::TerminalIO::out() << "Address: " << address_str << "\n"; - td::TerminalIO::out() << "Balance: " - << Grams{td::narrow_cast(state->balance_ * (state->balance_ > 0))} - << "\n"; - td::TerminalIO::out() << "Sync utime: " << state->sync_utime_ << "\n"; - td::TerminalIO::out() << "transaction.LT: " << state->last_transaction_id_->lt_ << "\n"; - td::TerminalIO::out() << "transaction.Hash: " << td::base64_encode(state->last_transaction_id_->hash_) - << "\n"; - td::TerminalIO::out() << to_string(state->account_state_); + print_full_account_state(state); return td::Unit(); })); } @@ -2090,14 +2096,7 @@ class TonlibCli : public td::actor::Actor { ton::move_tl_object_as(std::move(transaction_id))), promise.wrap([address_str](auto&& state) { td::TerminalIO::out() << "Address: " << address_str << "\n"; - td::TerminalIO::out() << "Balance: " - << Grams{td::narrow_cast(state->balance_ * (state->balance_ > 0))} - << "\n"; - td::TerminalIO::out() << "Sync utime: " << state->sync_utime_ << "\n"; - td::TerminalIO::out() << "transaction.LT: " << state->last_transaction_id_->lt_ << "\n"; - td::TerminalIO::out() << "transaction.Hash: " << td::base64_encode(state->last_transaction_id_->hash_) - << "\n"; - td::TerminalIO::out() << to_string(state->account_state_); + print_full_account_state(state); return td::Unit(); })); } @@ -2225,15 +2224,29 @@ class TonlibCli : public td::actor::Actor { td::StringBuilder sb; for (tonlib_api::object_ptr& t : res->transactions_) { td::int64 balance = 0; + std::map extra_currencies; balance += t->in_msg_->value_; + for (const auto& extra : t->in_msg_->extra_currencies_) { + extra_currencies[extra->id_] += extra->amount_; + } for (auto& ot : t->out_msgs_) { balance -= ot->value_; + for (const auto& extra : ot->extra_currencies_) { + extra_currencies[extra->id_] -= extra->amount_; + } } if (balance >= 0) { sb << Grams{td::uint64(balance)}; } else { sb << "-" << Grams{td::uint64(-balance)}; } + for (const auto& [id, amount] : extra_currencies) { + if (amount > 0) { + sb << " + " << amount << ".$" << id; + } else if (amount < 0) { + sb << " - " << -amount << ".$" << id; + } + } sb << " Fee: " << Grams{td::uint64(t->fee_)}; if (t->in_msg_->source_->account_address_.empty()) { sb << " External "; @@ -2263,6 +2276,9 @@ class TonlibCli : public td::actor::Actor { sb << " To " << ot->destination_->account_address_; } sb << " " << Grams{td::uint64(ot->value_)}; + for (const auto& extra : ot->extra_currencies_) { + sb << " + " << extra->amount_ << ".$" << extra->id_; + } print_msg_data(sb, ot->msg_data_); } sb << "\n"; @@ -2327,8 +2343,9 @@ class TonlibCli : public td::actor::Actor { } else { data = make_object(message.str()); } - messages.push_back( - make_object(std::move(address.address), "", amount.nano, std::move(data), -1)); + messages.push_back(make_object( + std::move(address.address), "", amount.nano, std::vector>{}, + std::move(data), -1)); return td::Status::OK(); }; diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 0149e06e1..ec6eb0137 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -7,22 +7,18 @@ endif() add_executable(generate-random-id generate-random-id.cpp ) target_link_libraries(generate-random-id tl_api ton_crypto keys adnl git) -target_include_directories(generate-random-id PUBLIC - $/..) +target_include_directories(generate-random-id PUBLIC $/..) add_executable(json2tlo json2tlo.cpp ) target_link_libraries(json2tlo tl_api ton_crypto keys git) -target_include_directories(json2tlo PUBLIC - $/..) +target_include_directories(json2tlo PUBLIC $/..) add_executable(pack-viewer pack-viewer.cpp ) target_link_libraries(pack-viewer tl_api ton_crypto keys validator tddb) -target_include_directories(pack-viewer PUBLIC - $/..) +target_include_directories(pack-viewer PUBLIC $/..) add_executable(opcode-timing opcode-timing.cpp ) target_link_libraries(opcode-timing ton_crypto) -target_include_directories(pack-viewer PUBLIC - $/..) +target_include_directories(pack-viewer PUBLIC $/..) install(TARGETS generate-random-id RUNTIME DESTINATION bin) diff --git a/validator-engine-console/CMakeLists.txt b/validator-engine-console/CMakeLists.txt index 634a5b8b4..d87d8a6f3 100644 --- a/validator-engine-console/CMakeLists.txt +++ b/validator-engine-console/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.5 FATAL_ERROR) add_executable (validator-engine-console validator-engine-console.cpp validator-engine-console.h validator-engine-console-query.cpp validator-engine-console-query.h ) -target_link_libraries(validator-engine-console tdutils tdactor adnllite tl_api tl_lite_api tl-lite-utils ton_crypto ton_block terminal git) +target_link_libraries(validator-engine-console tdactor adnllite tl_api tl_lite_api tl-lite-utils ton_crypto terminal git) install(TARGETS validator-engine-console RUNTIME DESTINATION bin) diff --git a/validator-engine-console/validator-engine-console-query.cpp b/validator-engine-console/validator-engine-console-query.cpp index 372fa8121..ff4e6e04e 100644 --- a/validator-engine-console/validator-engine-console-query.cpp +++ b/validator-engine-console/validator-engine-console-query.cpp @@ -842,23 +842,27 @@ td::Status GetOverlaysStatsQuery::receive(td::BufferSlice data) { td::StringBuilder sb; sb << "overlay_id: " << s->overlay_id_ << " adnl_id: " << s->adnl_id_ << " scope: " << s->scope_ << "\n"; sb << " nodes:\n"; - - td::uint32 overlay_t_out_bytes = 0; - td::uint32 overlay_t_out_pckts = 0; - td::uint32 overlay_t_in_bytes = 0; - td::uint32 overlay_t_in_pckts = 0; - + + auto print_traffic = [&](const char *name, const char *indent, + ton::tl_object_ptr &t) { + sb << indent << name << ":\n" + << indent << " out: " << t->t_out_bytes_ << " bytes/sec, " << t->t_out_pckts_ << " pckts/sec\n" + << indent << " in: " << t->t_in_bytes_ << " bytes/sec, " << t->t_in_pckts_ << " pckts/sec\n"; + }; for (auto &n : s->nodes_) { - sb << " adnl_id: " << n->adnl_id_ << " ip_addr: " << n->ip_addr_ << " broadcast_errors: " << n->bdcst_errors_ << " fec_broadcast_errors: " << n->fec_bdcst_errors_ << " last_in_query: " << n->last_in_query_ << " (" << time_to_human(n->last_in_query_) << ")" << " last_out_query: " << n->last_out_query_ << " (" << time_to_human(n->last_out_query_) << ")" << "\n throughput:\n out: " << n->t_out_bytes_ << " bytes/sec, " << n->t_out_pckts_ << " pckts/sec\n in: " << n->t_in_bytes_ << " bytes/sec, " << n->t_in_pckts_ << " pckts/sec\n"; - - overlay_t_out_bytes += n->t_out_bytes_; - overlay_t_out_pckts += n->t_out_pckts_; - - overlay_t_in_bytes += n->t_in_bytes_; - overlay_t_in_pckts += n->t_in_pckts_; + sb << " adnl_id: " << n->adnl_id_ << " ip_addr: " << n->ip_addr_ << " broadcast_errors: " << n->bdcst_errors_ + << " fec_broadcast_errors: " << n->fec_bdcst_errors_ << " last_in_query: " << n->last_in_query_ << " (" + << time_to_human(n->last_in_query_) << ")" + << " last_out_query: " << n->last_out_query_ << " (" << time_to_human(n->last_out_query_) << ")" + << "\n"; + sb << " is_neighbour: " << n->is_neighbour_ << " is_alive: " << n->is_alive_ + << " node_flags: " << n->node_flags_ << "\n"; + print_traffic("throughput", " ", n->traffic_); + print_traffic("throughput (responses only)", " ", n->traffic_responses_); } - sb << " total_throughput:\n out: " << overlay_t_out_bytes << " bytes/sec, " << overlay_t_out_pckts << " pckts/sec\n in: " << overlay_t_in_bytes << " bytes/sec, " << overlay_t_in_pckts << " pckts/sec\n"; - + print_traffic("total_throughput", " ", s->total_traffic_); + print_traffic("total_throughput (responses only)", " ", s->total_traffic_responses_); + sb << " stats:\n"; for (auto &t : s->stats_) { sb << " " << t->key_ << "\t" << t->value_ << "\n"; @@ -884,54 +888,64 @@ td::Status GetOverlaysStatsJsonQuery::receive(td::BufferSlice data) { TRY_RESULT_PREFIX(f, ton::fetch_tl_object(data.as_slice(), true), "received incorrect answer: "); std::ofstream sb(file_name_); - + sb << "[\n"; bool rtail = false; for (auto &s : f->overlays_) { - if(rtail) { + if (rtail) { sb << ",\n"; } else { rtail = true; } - - sb << "{\n \"overlay_id\": \"" << s->overlay_id_ << "\",\n \"adnl_id\": \"" << s->adnl_id_ << "\",\n \"scope\": " << s->scope_ << ",\n"; + + sb << "{\n \"overlay_id\": \"" << s->overlay_id_ << "\",\n \"adnl_id\": \"" << s->adnl_id_ + << "\",\n \"scope\": " << s->scope_ << ",\n"; sb << " \"nodes\": [\n"; - - td::uint32 overlay_t_out_bytes = 0; - td::uint32 overlay_t_out_pckts = 0; - td::uint32 overlay_t_in_bytes = 0; - td::uint32 overlay_t_in_pckts = 0; - + + auto print_traffic = [&](const char *name, + ton::tl_object_ptr &t) { + sb << "\"" << name << "\": { \"out_bytes_sec\": " << t->t_out_bytes_ << ", \"out_pckts_sec\": " << t->t_out_pckts_ + << ", \"in_bytes_sec\": " << t->t_in_bytes_ << ", \"in_pckts_sec\": " << t->t_in_pckts_ << " }"; + }; + bool tail = false; for (auto &n : s->nodes_) { - if(tail) { + if (tail) { sb << ",\n"; } else { tail = true; } - - sb << " {\n \"adnl_id\": \"" << n->adnl_id_ << "\",\n \"ip_addr\": \"" << n->ip_addr_ << "\",\n \"broadcast_errors\": " << n->bdcst_errors_ << ",\n \"fec_broadcast_errors\": " << n->fec_bdcst_errors_ << ",\n \"last_in_query_unix\": " << n->last_in_query_ << ",\n \"last_in_query_human\": \"" << time_to_human(n->last_in_query_) << "\",\n" << " \"last_out_query_unix\": " << n->last_out_query_ << ",\n \"last_out_query_human\": \"" << time_to_human(n->last_out_query_) << "\",\n" << "\n \"throughput\": { \"out_bytes_sec\": " << n->t_out_bytes_ << ", \"out_pckts_sec\": " << n->t_out_pckts_ << ", \"in_bytes_sec\": " << n->t_in_bytes_ << ", \"in_pckts_sec\": " << n->t_in_pckts_ << " }\n }"; - - overlay_t_out_bytes += n->t_out_bytes_; - overlay_t_out_pckts += n->t_out_pckts_; - - overlay_t_in_bytes += n->t_in_bytes_; - overlay_t_in_pckts += n->t_in_pckts_; + + sb << " {\n \"adnl_id\": \"" << n->adnl_id_ << "\",\n \"ip_addr\": \"" << n->ip_addr_ + << "\",\n \"broadcast_errors\": " << n->bdcst_errors_ + << ",\n \"fec_broadcast_errors\": " << n->fec_bdcst_errors_ + << ",\n \"last_in_query_unix\": " << n->last_in_query_ << ",\n \"last_in_query_human\": \"" + << time_to_human(n->last_in_query_) << "\",\n" + << " \"last_out_query_unix\": " << n->last_out_query_ << ",\n \"last_out_query_human\": \"" + << time_to_human(n->last_out_query_) << "\",\n" + << "\n "; + print_traffic("throughput", n->traffic_); + sb << ",\n "; + print_traffic("throughput_responses", n->traffic_responses_); + sb << "\n }"; } - sb << " ],\n"; - - sb << " \"total_throughput\": { \"out_bytes_sec\": " << overlay_t_out_bytes << ", \"out_pckts_sec\": " << overlay_t_out_pckts << ", \"in_bytes_sec\": " << overlay_t_in_bytes << ", \"in_pckts_sec\": " << overlay_t_in_pckts << " },\n"; - + sb << " ],\n "; + + print_traffic("total_throughput", s->total_traffic_); + sb << ",\n "; + print_traffic("total_throughput_responses", s->total_traffic_responses_); + sb << ",\n"; + sb << " \"stats\": {\n"; - + tail = false; for (auto &t : s->stats_) { - if(tail) { + if (tail) { sb << ",\n"; } else { tail = true; } - + sb << " \"" << t->key_ << "\": \"" << t->value_ << "\""; } sb << "\n }\n"; @@ -939,7 +953,7 @@ td::Status GetOverlaysStatsJsonQuery::receive(td::BufferSlice data) { } sb << "]\n"; sb << std::flush; - + td::TerminalIO::output(std::string("wrote stats to " + file_name_ + "\n")); return td::Status::OK(); } @@ -1008,6 +1022,32 @@ td::Status ImportShardOverlayCertificateQuery::receive(td::BufferSlice data) { td::TerminalIO::out() << "successfully sent certificate to overlay manager\n"; return td::Status::OK(); } +td::Status GetActorStatsQuery::run() { + auto r_file_name = tokenizer_.get_token(); + if (r_file_name.is_ok()) { + file_name_ = r_file_name.move_as_ok(); + } + return td::Status::OK(); +} +td::Status GetActorStatsQuery::send() { + auto b = ton::create_serialize_tl_object(); + td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise()); + return td::Status::OK(); +} + +td::Status GetActorStatsQuery::receive(td::BufferSlice data) { + TRY_RESULT_PREFIX(f, ton::fetch_tl_object(data.as_slice(), true), + "received incorrect answer: "); + if (file_name_.empty()) { + td::TerminalIO::out() << f->data_; + } else { + std::ofstream sb(file_name_); + sb << f->data_; + sb << std::flush; + td::TerminalIO::output(std::string("wrote stats to " + file_name_ + "\n")); + } + return td::Status::OK(); +} td::Status GetPerfTimerStatsJsonQuery::run() { TRY_RESULT_ASSIGN(file_name_, tokenizer_.get_token()); @@ -1266,13 +1306,21 @@ td::Status GetCollatorOptionsJsonQuery::receive(td::BufferSlice data) { td::Status GetAdnlStatsJsonQuery::run() { TRY_RESULT_ASSIGN(file_name_, tokenizer_.get_token()); + if (!tokenizer_.endl()) { + TRY_RESULT(s, tokenizer_.get_token()); + if (s == "all") { + all_ = true; + } else { + return td::Status::Error(PSTRING() << "unexpected token " << s); + } + } TRY_STATUS(tokenizer_.check_endl()); return td::Status::OK(); } td::Status GetAdnlStatsJsonQuery::send() { auto b = - ton::create_serialize_tl_object(); + ton::create_serialize_tl_object(all_); td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise()); return td::Status::OK(); } @@ -1287,13 +1335,21 @@ td::Status GetAdnlStatsJsonQuery::receive(td::BufferSlice data) { } td::Status GetAdnlStatsQuery::run() { + if (!tokenizer_.endl()) { + TRY_RESULT(s, tokenizer_.get_token()); + if (s == "all") { + all_ = true; + } else { + return td::Status::Error(PSTRING() << "unexpected token " << s); + } + } TRY_STATUS(tokenizer_.check_endl()); return td::Status::OK(); } td::Status GetAdnlStatsQuery::send() { auto b = - ton::create_serialize_tl_object(); + ton::create_serialize_tl_object(all_); td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise()); return td::Status::OK(); } diff --git a/validator-engine-console/validator-engine-console-query.h b/validator-engine-console/validator-engine-console-query.h index 6314d6199..0e21c9c21 100644 --- a/validator-engine-console/validator-engine-console-query.h +++ b/validator-engine-console/validator-engine-console-query.h @@ -1076,6 +1076,28 @@ class ImportShardOverlayCertificateQuery : public Query { std::string in_file_; }; +class GetActorStatsQuery : public Query { + public: + GetActorStatsQuery(td::actor::ActorId console, Tokenizer tokenizer) + : Query(console, std::move(tokenizer)) { + } + td::Status run() override; + td::Status send() override; + td::Status receive(td::BufferSlice data) override; + static std::string get_name() { + return "getactorstats"; + } + static std::string get_help() { + return "getactorstats []\tget actor stats and print it either in stdout or in "; + } + std::string name() const override { + return get_name(); + } + + private: + std::string file_name_; +}; + class GetPerfTimerStatsJsonQuery : public Query { public: GetPerfTimerStatsJsonQuery(td::actor::ActorId console, Tokenizer tokenizer) @@ -1305,7 +1327,8 @@ class GetAdnlStatsJsonQuery : public Query { return "getadnlstatsjson"; } static std::string get_help() { - return "getadnlstatsjson \tsave adnl stats to "; + return "getadnlstatsjson [all]\tsave adnl stats to . all - returns all peers (default - only " + "peers with traffic in the last 10 minutes)"; } std::string name() const override { return get_name(); @@ -1313,6 +1336,7 @@ class GetAdnlStatsJsonQuery : public Query { private: std::string file_name_; + bool all_ = false; }; class GetAdnlStatsQuery : public Query { @@ -1327,7 +1351,8 @@ class GetAdnlStatsQuery : public Query { return "getadnlstats"; } static std::string get_help() { - return "getadnlstats\tdisplay adnl stats"; + return "getadnlstats [all]\tdisplay adnl stats. all - returns all peers (default - only peers with traffic in the " + "last 10 minutes)"; } std::string name() const override { return get_name(); @@ -1335,4 +1360,5 @@ class GetAdnlStatsQuery : public Query { private: std::string file_name_; + bool all_ = false; }; diff --git a/validator-engine-console/validator-engine-console.cpp b/validator-engine-console/validator-engine-console.cpp index 1ec0f3803..59e2f2e8a 100644 --- a/validator-engine-console/validator-engine-console.cpp +++ b/validator-engine-console/validator-engine-console.cpp @@ -140,6 +140,7 @@ void ValidatorEngineConsole::run() { add_query_runner(std::make_unique>()); add_query_runner(std::make_unique>()); add_query_runner(std::make_unique>()); + add_query_runner(std::make_unique>()); add_query_runner(std::make_unique>()); add_query_runner(std::make_unique>()); add_query_runner(std::make_unique>()); diff --git a/validator-engine/validator-engine.cpp b/validator-engine/validator-engine.cpp index bb7574105..8b35b2b1f 100644 --- a/validator-engine/validator-engine.cpp +++ b/validator-engine/validator-engine.cpp @@ -1432,7 +1432,11 @@ td::Status ValidatorEngine::load_global_config() { if (!session_logs_file_.empty()) { validator_options_.write().set_session_logs_file(session_logs_file_); } + if (celldb_in_memory_) { + celldb_compress_depth_ = 0; + } validator_options_.write().set_celldb_compress_depth(celldb_compress_depth_); + validator_options_.write().set_celldb_in_memory(celldb_in_memory_); validator_options_.write().set_max_open_archive_files(max_open_archive_files_); validator_options_.write().set_archive_preload_period(archive_preload_period_); validator_options_.write().set_disable_rocksdb_stats(disable_rocksdb_stats_); @@ -1471,11 +1475,11 @@ td::Status ValidatorEngine::load_global_config() { } validator_options_.write().set_hardforks(std::move(h)); - auto r_total_ram = td::get_total_ram(); - if (r_total_ram.is_error()) { - LOG(ERROR) << "Failed to get total RAM size: " << r_total_ram.move_as_error(); + auto r_total_mem_stat = td::get_total_mem_stat(); + if (r_total_mem_stat.is_error()) { + LOG(ERROR) << "Failed to get total RAM size: " << r_total_mem_stat.move_as_error(); } else { - td::uint64 total_ram = r_total_ram.move_as_ok(); + td::uint64 total_ram = r_total_mem_stat.ok().total_ram; LOG(WARNING) << "Total RAM = " << td::format::as_size(total_ram); if (total_ram >= (90ULL << 30)) { fast_state_serializer_enabled_ = true; @@ -3540,6 +3544,31 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_getOverla }); } +void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_getActorTextStats &query, td::BufferSlice data, + ton::PublicKeyHash src, td::uint32 perm, td::Promise promise) { + if (!(perm & ValidatorEnginePermissions::vep_default)) { + promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized"))); + return; + } + + if (validator_manager_.empty()) { + promise.set_value( + create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "validator manager not started"))); + return; + } + + auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_value(create_control_query_error(R.move_as_error())); + } else { + auto r = R.move_as_ok(); + promise.set_value(ton::create_serialize_tl_object(std::move(r))); + } + }); + td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::prepare_actor_stats, + std::move(P)); +} + void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_getPerfTimerStats &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise) { if (!(perm & ValidatorEnginePermissions::vep_default)) { @@ -3753,9 +3782,8 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_addCustom }); } -void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_delCustomOverlay &query, - td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, - td::Promise promise) { +void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_delCustomOverlay &query, td::BufferSlice data, + ton::PublicKeyHash src, td::uint32 perm, td::Promise promise) { if (!(perm & ValidatorEnginePermissions::vep_modify)) { promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized"))); return; @@ -3795,8 +3823,8 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_showCusto return; } - promise.set_value(ton::serialize_tl_object( - custom_overlays_config_, true)); + promise.set_value( + ton::serialize_tl_object(custom_overlays_config_, true)); } void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_setStateSerializerEnabled &query, @@ -3885,7 +3913,7 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_getAdnlSt return; } td::actor::send_closure( - adnl_, &ton::adnl::Adnl::get_stats, + adnl_, &ton::adnl::Adnl::get_stats, query.all_, [promise = std::move(promise)](td::Result> R) mutable { if (R.is_ok()) { promise.set_value(ton::serialize_tl_object(R.move_as_ok(), true)); @@ -4223,7 +4251,8 @@ int main(int argc, char *argv[]) { return td::Status::OK(); }); p.add_checked_option( - '\0', "max-archive-fd", "limit for a number of open file descriptirs in archive manager. 0 is unlimited (default)", + '\0', "max-archive-fd", + "limit for a number of open file descriptirs in archive manager. 0 is unlimited (default)", [&](td::Slice s) -> td::Status { TRY_RESULT(v, td::to_integer_safe(s)); acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_max_open_archive_files, v); }); @@ -4258,13 +4287,23 @@ int main(int argc, char *argv[]) { acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_celldb_cache_size, v); }); return td::Status::OK(); }); + p.add_option('\0', "celldb-direct-io", + "enable direct I/O mode for RocksDb in CellDb (doesn't apply when celldb cache is < 30G)", [&]() { + acts.push_back([&x]() { td::actor::send_closure(x, &ValidatorEngine::set_celldb_direct_io, true); }); + }); + p.add_option('\0', "celldb-preload-all", + "preload all cells from CellDb on startup (recommended to use with big enough celldb-cache-size and " + "celldb-direct-io)", + [&]() { + acts.push_back([&x]() { td::actor::send_closure(x, &ValidatorEngine::set_celldb_preload_all, true); }); + }); + p.add_option( - '\0', "celldb-direct-io", "enable direct I/O mode for RocksDb in CellDb (doesn't apply when celldb cache is < 30G)", - [&]() { acts.push_back([&x]() { td::actor::send_closure(x, &ValidatorEngine::set_celldb_direct_io, true); }); }); - p.add_option( - '\0', "celldb-preload-all", - "preload all cells from CellDb on startup (recommended to use with big enough celldb-cache-size and celldb-direct-io)", - [&]() { acts.push_back([&x]() { td::actor::send_closure(x, &ValidatorEngine::set_celldb_preload_all, true); }); }); + '\0', "celldb-in-memory", + "store all cells in-memory, much faster but requires a lot of RAM. RocksDb is still used as persistent storage", + [&]() { + acts.push_back([&x]() { td::actor::send_closure(x, &ValidatorEngine::set_celldb_in_memory, true); }); + }); p.add_checked_option( '\0', "catchain-max-block-delay", "delay before creating a new catchain block, in seconds (default: 0.4)", [&](td::Slice s) -> td::Status { @@ -4319,7 +4358,9 @@ int main(int argc, char *argv[]) { } if (need_scheduler_status_flag.exchange(false)) { LOG(ERROR) << "DUMPING SCHEDULER STATISTICS"; - scheduler.get_debug().dump(); + td::StringBuilder sb; + scheduler.get_debug().dump(sb); + LOG(ERROR) << "GOT SCHEDULER STATISTICS\n" << sb.as_cslice(); } if (rotate_logs_flags.exchange(false)) { if (td::log_interface) { diff --git a/validator-engine/validator-engine.hpp b/validator-engine/validator-engine.hpp index 2e94dd1ef..50cd5a323 100644 --- a/validator-engine/validator-engine.hpp +++ b/validator-engine/validator-engine.hpp @@ -214,6 +214,7 @@ class ValidatorEngine : public td::actor::Actor { td::optional celldb_cache_size_ = 1LL << 30; bool celldb_direct_io_ = false; bool celldb_preload_all_ = false; + bool celldb_in_memory_ = false; td::optional catchain_max_block_delay_, catchain_max_block_delay_slow_; bool read_config_ = false; bool started_keyring_ = false; @@ -297,6 +298,9 @@ class ValidatorEngine : public td::actor::Actor { void set_celldb_preload_all(bool value) { celldb_preload_all_ = value; } + void set_celldb_in_memory(bool value) { + celldb_in_memory_ = value; + } void set_catchain_max_block_delay(double value) { catchain_max_block_delay_ = value; } @@ -474,6 +478,8 @@ class ValidatorEngine : public td::actor::Actor { ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); void run_control_query(ton::ton_api::engine_validator_getOverlaysStats &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); + void run_control_query(ton::ton_api::engine_validator_getActorTextStats &query, td::BufferSlice data, + ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); void run_control_query(ton::ton_api::engine_validator_getPerfTimerStats &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); void run_control_query(ton::ton_api::engine_validator_getShardOutQueueSize &query, td::BufferSlice data, diff --git a/validator-session/CMakeLists.txt b/validator-session/CMakeLists.txt index 41911a8b0..6b4e8f687 100644 --- a/validator-session/CMakeLists.txt +++ b/validator-session/CMakeLists.txt @@ -28,5 +28,4 @@ target_include_directories(validatorsession PUBLIC $/.. ${OPENSSL_INCLUDE_DIR} ) -target_link_libraries(validatorsession PRIVATE tdutils tdactor adnl rldp tl_api dht tdfec - overlay catchain) +target_link_libraries(validatorsession PRIVATE tdutils tdactor adnl rldp tl_api dht tdfec overlay catchain) diff --git a/validator-session/candidate-serializer.cpp b/validator-session/candidate-serializer.cpp index a85c4b72e..4442504ed 100644 --- a/validator-session/candidate-serializer.cpp +++ b/validator-session/candidate-serializer.cpp @@ -22,27 +22,15 @@ namespace ton::validatorsession { -td::Result serialize_candidate(const tl_object_ptr &block, +td::Result serialize_candidate(const tl_object_ptr& block, bool compression_enabled) { if (!compression_enabled) { return serialize_tl_object(block, true); } - vm::BagOfCells boc1, boc2; - TRY_STATUS(boc1.deserialize(block->data_)); - if (boc1.get_root_count() != 1) { - return td::Status::Error("block candidate should have exactly one root"); - } - std::vector> roots = {boc1.get_root_cell()}; - TRY_STATUS(boc2.deserialize(block->collated_data_)); - for (int i = 0; i < boc2.get_root_count(); ++i) { - roots.push_back(boc2.get_root_cell(i)); - } - TRY_RESULT(data, vm::std_boc_serialize_multi(std::move(roots), 2)); - td::BufferSlice compressed = td::lz4_compress(data); - LOG(VALIDATOR_SESSION_DEBUG) << "Compressing block candidate: " << block->data_.size() + block->collated_data_.size() - << " -> " << compressed.size(); + size_t decompressed_size; + TRY_RESULT(compressed, compress_candidate_data(block->data_, block->collated_data_, decompressed_size)) return create_serialize_tl_object( - 0, block->src_, block->round_, block->root_hash_, (int)data.size(), std::move(compressed)); + 0, block->src_, block->round_, block->root_hash_, (int)decompressed_size, std::move(compressed)); } td::Result> deserialize_candidate(td::Slice data, @@ -55,8 +43,34 @@ td::Result> deserialize_candi if (f->decompressed_size_ > max_decompressed_data_size) { return td::Status::Error("decompressed size is too big"); } - TRY_RESULT(decompressed, td::lz4_decompress(f->data_, f->decompressed_size_)); - if (decompressed.size() != (size_t)f->decompressed_size_) { + TRY_RESULT(p, decompress_candidate_data(f->data_, f->decompressed_size_)); + return create_tl_object(f->src_, f->round_, f->root_hash_, std::move(p.first), + std::move(p.second)); +} + +td::Result compress_candidate_data(td::Slice block, td::Slice collated_data, + size_t& decompressed_size) { + vm::BagOfCells boc1, boc2; + TRY_STATUS(boc1.deserialize(block)); + if (boc1.get_root_count() != 1) { + return td::Status::Error("block candidate should have exactly one root"); + } + std::vector> roots = {boc1.get_root_cell()}; + TRY_STATUS(boc2.deserialize(collated_data)); + for (int i = 0; i < boc2.get_root_count(); ++i) { + roots.push_back(boc2.get_root_cell(i)); + } + TRY_RESULT(data, vm::std_boc_serialize_multi(std::move(roots), 2)); + decompressed_size = data.size(); + td::BufferSlice compressed = td::lz4_compress(data); + LOG(DEBUG) << "Compressing block candidate: " << block.size() + collated_data.size() << " -> " << compressed.size(); + return compressed; +} + +td::Result> decompress_candidate_data(td::Slice compressed, + int decompressed_size) { + TRY_RESULT(decompressed, td::lz4_decompress(compressed, decompressed_size)); + if (decompressed.size() != (size_t)decompressed_size) { return td::Status::Error("decompressed size mismatch"); } TRY_RESULT(roots, vm::std_boc_deserialize_multi(decompressed)); @@ -66,10 +80,9 @@ td::Result> deserialize_candi TRY_RESULT(block_data, vm::std_boc_serialize(roots[0], 31)); roots.erase(roots.begin()); TRY_RESULT(collated_data, vm::std_boc_serialize_multi(std::move(roots), 31)); - LOG(VALIDATOR_SESSION_DEBUG) << "Decompressing block candidate: " << f->data_.size() << " -> " - << block_data.size() + collated_data.size(); - return create_tl_object(f->src_, f->round_, f->root_hash_, std::move(block_data), - std::move(collated_data)); + LOG(DEBUG) << "Decompressing block candidate: " << compressed.size() << " -> " + << block_data.size() + collated_data.size(); + return std::make_pair(std::move(block_data), std::move(collated_data)); } } // namespace ton::validatorsession diff --git a/validator-session/candidate-serializer.h b/validator-session/candidate-serializer.h index 030a412c0..e88376cd7 100644 --- a/validator-session/candidate-serializer.h +++ b/validator-session/candidate-serializer.h @@ -20,10 +20,15 @@ namespace ton::validatorsession { -td::Result serialize_candidate(const tl_object_ptr &block, +td::Result serialize_candidate(const tl_object_ptr& block, bool compression_enabled); td::Result> deserialize_candidate(td::Slice data, bool compression_enabled, int max_decompressed_data_size); +td::Result compress_candidate_data(td::Slice block, td::Slice collated_data, + size_t& decompressed_size); +td::Result> decompress_candidate_data(td::Slice compressed, + int decompressed_size); + } // namespace ton::validatorsession diff --git a/validator-session/validator-session-description.cpp b/validator-session/validator-session-description.cpp index 34dc90212..c747e26e7 100644 --- a/validator-session/validator-session-description.cpp +++ b/validator-session/validator-session-description.cpp @@ -72,6 +72,11 @@ td::uint32 ValidatorSessionDescriptionImpl::get_max_priority() const { return opts_.round_candidates - 1; } +td::uint32 ValidatorSessionDescriptionImpl::get_node_by_priority(td::uint32 round, td::uint32 priority) const { + CHECK(priority <= get_max_priority()); + return (round + priority) % get_total_nodes(); +} + ValidatorSessionCandidateId ValidatorSessionDescriptionImpl::candidate_id( td::uint32 src_idx, ValidatorSessionRootHash root_hash, ValidatorSessionFileHash file_hash, ValidatorSessionCollatedDataFileHash collated_data_file_hash) const { diff --git a/validator-session/validator-session-description.h b/validator-session/validator-session-description.h index 60e006d08..ac5043125 100644 --- a/validator-session/validator-session-description.h +++ b/validator-session/validator-session-description.h @@ -85,6 +85,7 @@ class ValidatorSessionDescription { virtual ValidatorWeight get_total_weight() const = 0; virtual td::int32 get_node_priority(td::uint32 src_idx, td::uint32 round) const = 0; virtual td::uint32 get_max_priority() const = 0; + virtual td::uint32 get_node_by_priority(td::uint32 round, td::uint32 priority) const = 0; virtual td::uint32 get_unixtime(td::uint64 t) const = 0; virtual td::uint32 get_attempt_seqno(td::uint64 t) const = 0; virtual td::uint32 get_self_idx() const = 0; diff --git a/validator-session/validator-session-description.hpp b/validator-session/validator-session-description.hpp index 5e09c694a..0fbb4edeb 100644 --- a/validator-session/validator-session-description.hpp +++ b/validator-session/validator-session-description.hpp @@ -114,6 +114,7 @@ class ValidatorSessionDescriptionImpl : public ValidatorSessionDescription { } td::int32 get_node_priority(td::uint32 src_idx, td::uint32 round) const override; td::uint32 get_max_priority() const override; + td::uint32 get_node_by_priority(td::uint32 round, td::uint32 priority) const override; td::uint32 get_unixtime(td::uint64 ts) const override { return static_cast(ts >> 32); } diff --git a/validator-session/validator-session-types.h b/validator-session/validator-session-types.h index 78a9b2460..7147bf2d3 100644 --- a/validator-session/validator-session-types.h +++ b/validator-session/validator-session-types.h @@ -178,6 +178,12 @@ struct EndValidatorGroupStats { std::vector nodes; }; +struct BlockSourceInfo { + td::uint32 round, first_block_round; + PublicKey source; + td::int32 source_priority; +}; + } // namespace validatorsession } // namespace ton diff --git a/validator-session/validator-session.cpp b/validator-session/validator-session.cpp index 246f7e58a..3a913990c 100644 --- a/validator-session/validator-session.cpp +++ b/validator-session/validator-session.cpp @@ -21,6 +21,7 @@ #include "td/utils/crypto.h" #include "candidate-serializer.h" #include "td/utils/overloaded.h" +#include "ton/ton-tl.hpp" namespace ton { @@ -41,7 +42,7 @@ void ValidatorSessionImpl::process_blocks(std::vector on_new_round(real_state_->cur_round_seqno()); } - td::uint32 cnt = 0; + [[maybe_unused]] td::uint32 cnt = 0; auto ts = description().get_ts(); auto att = description().get_attempt_seqno(ts); std::vector> msgs; @@ -552,7 +553,9 @@ void ValidatorSessionImpl::check_generate_slot() { LOG(WARNING) << print_id << ": failed to generate block candidate: " << R.move_as_error(); } }); - callback_->on_generate_slot(cur_round_, std::move(P)); + callback_->on_generate_slot( + BlockSourceInfo{cur_round_, first_block_round_, description().get_source_public_key(local_idx()), priority}, + std::move(P)); } else { alarm_timestamp().relax(t); } @@ -630,8 +633,10 @@ void ValidatorSessionImpl::try_approve_block(const SentBlock *block) { }); pending_approve_.insert(block_id); - callback_->on_candidate(cur_round_, description().get_source_public_key(block->get_src_idx()), B->root_hash_, - B->data_.clone(), B->collated_data_.clone(), std::move(P)); + callback_->on_candidate( + BlockSourceInfo{cur_round_, first_block_round_, description().get_source_public_key(block->get_src_idx()), + description().get_node_priority(block->get_src_idx(), cur_round_)}, + B->root_hash_, B->data_.clone(), B->collated_data_.clone(), std::move(P)); } else if (T.is_in_past()) { if (!active_requests_.count(block_id)) { auto v = virtual_state_->get_block_approvers(description(), block_id); @@ -904,15 +909,19 @@ void ValidatorSessionImpl::on_new_round(td::uint32 round) { stats.rounds.pop_back(); } + BlockSourceInfo source_info{cur_round_, first_block_round_, + description().get_source_public_key(block->get_src_idx()), + description().get_node_priority(block->get_src_idx(), cur_round_)}; if (it == blocks_.end()) { - callback_->on_block_committed(cur_round_, description().get_source_public_key(block->get_src_idx()), - block->get_root_hash(), block->get_file_hash(), td::BufferSlice(), - std::move(export_sigs), std::move(export_approve_sigs), std::move(stats)); + callback_->on_block_committed(std::move(source_info), block->get_root_hash(), block->get_file_hash(), + td::BufferSlice(), std::move(export_sigs), std::move(export_approve_sigs), + std::move(stats)); } else { - callback_->on_block_committed(cur_round_, description().get_source_public_key(block->get_src_idx()), - block->get_root_hash(), block->get_file_hash(), it->second->data_.clone(), - std::move(export_sigs), std::move(export_approve_sigs), std::move(stats)); + callback_->on_block_committed(std::move(source_info), block->get_root_hash(), block->get_file_hash(), + it->second->data_.clone(), std::move(export_sigs), std::move(export_approve_sigs), + std::move(stats)); } + first_block_round_ = cur_round_ + 1; } cur_round_++; if (have_block) { @@ -991,6 +1000,9 @@ ValidatorSessionImpl::ValidatorSessionImpl(catchain::CatChainSessionId session_i } void ValidatorSessionImpl::start() { + round_started_at_ = td::Timestamp::now(); + round_debug_at_ = td::Timestamp::in(60.0); + stats_init(); started_ = true; VLOG(VALIDATOR_SESSION_NOTICE) << this << ": started"; @@ -1096,8 +1108,6 @@ void ValidatorSessionImpl::start_up() { check_all(); td::actor::send_closure(rldp_, &rldp::Rldp::add_id, description().get_source_adnl_id(local_idx())); - - stats_init(); } void ValidatorSessionImpl::stats_init() { diff --git a/validator-session/validator-session.h b/validator-session/validator-session.h index e60330b09..b099d65ec 100644 --- a/validator-session/validator-session.h +++ b/validator-session/validator-session.h @@ -85,11 +85,10 @@ class ValidatorSession : public td::actor::Actor { class Callback { public: - virtual void on_candidate(td::uint32 round, PublicKey source, ValidatorSessionRootHash root_hash, - td::BufferSlice data, td::BufferSlice collated_data, - td::Promise promise) = 0; - virtual void on_generate_slot(td::uint32 round, td::Promise promise) = 0; - virtual void on_block_committed(td::uint32 round, PublicKey source, ValidatorSessionRootHash root_hash, + virtual void on_candidate(BlockSourceInfo source_info, ValidatorSessionRootHash root_hash, td::BufferSlice data, + td::BufferSlice collated_data, td::Promise promise) = 0; + virtual void on_generate_slot(BlockSourceInfo source_info, td::Promise promise) = 0; + virtual void on_block_committed(BlockSourceInfo source_info, ValidatorSessionRootHash root_hash, ValidatorSessionFileHash file_hash, td::BufferSlice data, std::vector> signatures, std::vector> approve_signatures, diff --git a/validator-session/validator-session.hpp b/validator-session/validator-session.hpp index 39f196d8b..690346f70 100644 --- a/validator-session/validator-session.hpp +++ b/validator-session/validator-session.hpp @@ -53,7 +53,7 @@ class ValidatorSessionImpl : public ValidatorSession { const ValidatorSessionState *real_state_ = nullptr; const ValidatorSessionState *virtual_state_ = nullptr; - td::uint32 cur_round_ = 0; + td::uint32 cur_round_ = 0, first_block_round_ = 0; td::Timestamp round_started_at_ = td::Timestamp::never(); td::Timestamp round_debug_at_ = td::Timestamp::never(); std::set pending_approve_; diff --git a/validator/CMakeLists.txt b/validator/CMakeLists.txt index 20462caeb..d5ba00e39 100644 --- a/validator/CMakeLists.txt +++ b/validator/CMakeLists.txt @@ -200,14 +200,10 @@ target_include_directories(full-node PUBLIC ${OPENSSL_INCLUDE_DIR} ) -target_link_libraries(validator PRIVATE tdutils tdactor adnl rldp tl_api dht tdfec - overlay catchain validatorsession ton_crypto ton_block ton_db) +target_link_libraries(validator PRIVATE tdactor adnl rldp tl_api dht tdfec overlay catchain validatorsession ton_db) -target_link_libraries(validator-disk PRIVATE tdutils tdactor adnl rldp tl_api dht tdfec - overlay catchain validatorsession ton_crypto ton_block ton_db) +target_link_libraries(validator-disk PRIVATE tdactor adnl rldp tl_api dht tdfec overlay catchain validatorsession 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(validator-hardfork PRIVATE tdactor adnl rldp tl_api dht tdfec overlay catchain validatorsession ton_db) -target_link_libraries(full-node PRIVATE tdutils tdactor adnl rldp rldp2 tl_api dht tdfec - overlay catchain validatorsession ton_crypto ton_block ton_db) +target_link_libraries(full-node PRIVATE tdactor adnl rldp rldp2 tl_api dht tdfec overlay catchain validatorsession ton_db) diff --git a/validator/db/archive-manager.cpp b/validator/db/archive-manager.cpp index 14d3ec469..71b3b73de 100644 --- a/validator/db/archive-manager.cpp +++ b/validator/db/archive-manager.cpp @@ -32,11 +32,11 @@ std::string PackageId::path() const { return "/files/packages/"; } else if (key) { char s[24]; - sprintf(s, "key%03d", id / 1000000); + snprintf(s, sizeof(s), "key%03d", id / 1000000); return PSTRING() << "/archive/packages/" << s << "/"; } else { char s[20]; - sprintf(s, "arch%04d", id / 100000); + snprintf(s, sizeof(s), "arch%04d", id / 100000); return PSTRING() << "/archive/packages/" << s << "/"; } } @@ -46,11 +46,11 @@ std::string PackageId::name() const { return PSTRING() << "temp.archive." << id; } else if (key) { char s[20]; - sprintf(s, "%06d", id); + snprintf(s, sizeof(s), "%06d", id); return PSTRING() << "key.archive." << s; } else { char s[10]; - sprintf(s, "%05d", id); + snprintf(s, sizeof(s), "%05d", id); return PSTRING() << "archive." << s; } } @@ -342,19 +342,19 @@ void ArchiveManager::add_zero_state(BlockIdExt block_id, td::BufferSlice data, t void ArchiveManager::add_persistent_state(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::BufferSlice data, td::Promise promise) { auto create_writer = [&](std::string path, td::Promise P) { - td::actor::create_actor("writefile", db_root_ + "/archive/tmp/", - std::move(path), std::move(data), std::move(P)) + td::actor::create_actor("writefile", db_root_ + "/archive/tmp/", std::move(path), std::move(data), + std::move(P)) .release(); }; add_persistent_state_impl(block_id, masterchain_block_id, std::move(promise), std::move(create_writer)); } void ArchiveManager::add_persistent_state_gen(BlockIdExt block_id, BlockIdExt masterchain_block_id, - std::function write_state, + std::function write_state, td::Promise promise) { auto create_writer = [&](std::string path, td::Promise P) { - td::actor::create_actor("writefile", db_root_ + "/archive/tmp/", - std::move(path), std::move(write_state), std::move(P)) + td::actor::create_actor("writefile", db_root_ + "/archive/tmp/", std::move(path), + std::move(write_state), std::move(P)) .release(); }; add_persistent_state_impl(block_id, masterchain_block_id, std::move(promise), std::move(create_writer)); @@ -624,8 +624,8 @@ void ArchiveManager::load_package(PackageId id) { } } - desc.file = - td::actor::create_actor("slice", id.id, id.key, id.temp, false, db_root_, archive_lru_.get(), statistics_); + desc.file = td::actor::create_actor("slice", id.id, id.key, id.temp, false, db_root_, + archive_lru_.get(), statistics_); m.emplace(id, std::move(desc)); update_permanent_slices(); @@ -659,8 +659,8 @@ const ArchiveManager::FileDescription *ArchiveManager::add_file_desc(ShardIdFull FileDescription new_desc{id, false}; td::mkdir(db_root_ + id.path()).ensure(); std::string prefix = PSTRING() << db_root_ << id.path() << id.name(); - new_desc.file = - td::actor::create_actor("slice", id.id, id.key, id.temp, false, db_root_, archive_lru_.get(), statistics_); + new_desc.file = td::actor::create_actor("slice", id.id, id.key, id.temp, false, db_root_, + archive_lru_.get(), statistics_); const FileDescription &desc = f.emplace(id, std::move(new_desc)); if (!id.temp) { update_desc(f, desc, shard, seqno, ts, lt); @@ -940,7 +940,8 @@ void ArchiveManager::start_up() { void ArchiveManager::alarm() { alarm_timestamp() = td::Timestamp::in(60.0); auto stats = statistics_.to_string_and_reset(); - auto to_file_r = td::FileFd::open(db_root_ + "/db_stats.txt", td::FileFd::Truncate | td::FileFd::Create | td::FileFd::Write, 0644); + auto to_file_r = + td::FileFd::open(db_root_ + "/db_stats.txt", td::FileFd::Truncate | td::FileFd::Create | td::FileFd::Write, 0644); if (to_file_r.is_error()) { LOG(ERROR) << "Failed to open db_stats.txt: " << to_file_r.move_as_error(); return; @@ -1034,7 +1035,7 @@ void ArchiveManager::persistent_state_gc(std::pair last) { } if (res != 0) { delay_action([key, SelfId = actor_id( - this)]() { td::actor::send_closure(SelfId, &ArchiveManager::persistent_state_gc, key); }, + this)]() { td::actor::send_closure(SelfId, &ArchiveManager::persistent_state_gc, key); }, td::Timestamp::in(1.0)); return; } @@ -1051,7 +1052,7 @@ void ArchiveManager::persistent_state_gc(std::pair last) { } if (!allow_delete) { delay_action([key, SelfId = actor_id( - this)]() { td::actor::send_closure(SelfId, &ArchiveManager::persistent_state_gc, key); }, + this)]() { td::actor::send_closure(SelfId, &ArchiveManager::persistent_state_gc, key); }, td::Timestamp::in(1.0)); return; } @@ -1082,9 +1083,9 @@ void ArchiveManager::got_gc_masterchain_handle(ConstBlockHandle handle, std::pai td::unlink(db_root_ + "/archive/states/" + F.filename_short()).ignore(); perm_states_.erase(it); } - delay_action([key, SelfId = actor_id( - this)]() { td::actor::send_closure(SelfId, &ArchiveManager::persistent_state_gc, key); }, - td::Timestamp::in(1.0)); + delay_action( + [key, SelfId = actor_id(this)]() { td::actor::send_closure(SelfId, &ArchiveManager::persistent_state_gc, key); }, + td::Timestamp::in(1.0)); } PackageId ArchiveManager::get_temp_package_id() const { diff --git a/validator/db/archive-slice.cpp b/validator/db/archive-slice.cpp index d392431a9..83fe144b4 100644 --- a/validator/db/archive-slice.cpp +++ b/validator/db/archive-slice.cpp @@ -768,22 +768,19 @@ void destroy_db(std::string name, td::uint32 attempt, td::Promise prom promise.set_value(td::Unit()); return; } - if (S.is_error() && attempt > 0 && attempt % 64 == 0) { + if (attempt > 0 && attempt % 64 == 0) { LOG(ERROR) << "failed to destroy index " << name << ": " << S; } else { LOG(DEBUG) << "failed to destroy index " << name << ": " << S; } delay_action( - [name, attempt, promise = std::move(promise)]() mutable { destroy_db(name, attempt, std::move(promise)); }, + [name, attempt, promise = std::move(promise)]() mutable { destroy_db(name, attempt + 1, std::move(promise)); }, td::Timestamp::in(1.0)); } } // namespace void ArchiveSlice::destroy(td::Promise promise) { before_query(); - td::MultiPromise mp; - auto ig = mp.init_guard(); - ig.add_promise(std::move(promise)); destroyed_ = true; for (auto &p : packages_) { @@ -795,10 +792,8 @@ void ArchiveSlice::destroy(td::Promise promise) { packages_.clear(); kv_ = nullptr; - PackageId p_id{archive_id_, key_blocks_only_, temp_}; - std::string db_path = PSTRING() << db_root_ << p_id.path() << p_id.name() << ".index"; - delay_action([name = db_path, attempt = 0, - promise = ig.get_promise()]() mutable { destroy_db(name, attempt, std::move(promise)); }, + delay_action([name = db_path_, attempt = 0, + promise = std::move(promise)]() mutable { destroy_db(name, attempt, std::move(promise)); }, td::Timestamp::in(0.0)); } diff --git a/validator/db/celldb.cpp b/validator/db/celldb.cpp index 1701ae588..9dcecdb35 100644 --- a/validator/db/celldb.cpp +++ b/validator/db/celldb.cpp @@ -17,10 +17,12 @@ Copyright 2017-2020 Telegram Systems LLP */ #include "celldb.hpp" + +#include "files-async.hpp" #include "rootdb.hpp" #include "td/db/RocksDb.h" -#include "td/utils/filesystem.h" +#include "rocksdb/utilities/optimistic_transaction_db.h" #include "ton/ton-tl.hpp" #include "ton/ton-io.hpp" @@ -29,7 +31,6 @@ namespace ton { namespace validator { - class CellDbAsyncExecutor : public vm::DynamicBagOfCellsDb::AsyncExecutor { public: explicit CellDbAsyncExecutor(td::actor::ActorId cell_db) : cell_db_(std::move(cell_db)) { @@ -38,11 +39,13 @@ class CellDbAsyncExecutor : public vm::DynamicBagOfCellsDb::AsyncExecutor { void execute_async(std::function f) override { class Runner : public td::actor::Actor { public: - explicit Runner(std::function f) : f_(std::move(f)) {} + explicit Runner(std::function f) : f_(std::move(f)) { + } void start_up() override { f_(); stop(); } + private: std::function f_; }; @@ -52,6 +55,7 @@ class CellDbAsyncExecutor : public vm::DynamicBagOfCellsDb::AsyncExecutor { void execute_sync(std::function f) override { td::actor::send_closure(cell_db_, &CellDbBase::execute_sync, std::move(f)); } + private: td::actor::ActorId cell_db_; }; @@ -97,13 +101,30 @@ void CellDbIn::start_up() { LOG(WARNING) << "Set CellDb block cache size to " << td::format::as_size(opts_->get_celldb_cache_size().value()); } db_options.use_direct_reads = opts_->get_celldb_direct_io(); - cell_db_ = std::make_shared(td::RocksDb::open(path_, std::move(db_options)).move_as_ok()); + if (opts_->get_celldb_in_memory()) { + td::RocksDbOptions read_db_options; + read_db_options.use_direct_reads = true; + read_db_options.no_block_cache = true; + read_db_options.block_cache = {}; + LOG(WARNING) << "Loading all cells in memory (because of --celldb-in-memory)"; + td::Timer timer; + auto read_cell_db = + std::make_shared(td::RocksDb::open(path_, std::move(read_db_options)).move_as_ok()); + boc_ = vm::DynamicBagOfCellsDb::create_in_memory(read_cell_db.get(), {}); + in_memory_load_time_ = timer.elapsed(); + td::actor::send_closure(parent_, &CellDb::set_in_memory_boc, boc_); + } - boc_ = vm::DynamicBagOfCellsDb::create(); - boc_->set_celldb_compress_depth(opts_->get_celldb_compress_depth()); - boc_->set_loader(std::make_unique(cell_db_->snapshot(), on_load_callback_)).ensure(); - td::actor::send_closure(parent_, &CellDb::update_snapshot, cell_db_->snapshot()); + auto rocks_db = std::make_shared(td::RocksDb::open(path_, std::move(db_options)).move_as_ok()); + rocks_db_ = rocks_db->raw_db(); + cell_db_ = std::move(rocks_db); + if (!opts_->get_celldb_in_memory()) { + boc_ = vm::DynamicBagOfCellsDb::create(); + boc_->set_celldb_compress_depth(opts_->get_celldb_compress_depth()); + boc_->set_loader(std::make_unique(cell_db_->snapshot(), on_load_callback_)).ensure(); + td::actor::send_closure(parent_, &CellDb::update_snapshot, cell_db_->snapshot()); + } alarm_timestamp() = td::Timestamp::in(10.0); @@ -117,31 +138,56 @@ void CellDbIn::start_up() { if (opts_->get_celldb_preload_all()) { // Iterate whole DB in a separate thread - delay_action([snapshot = cell_db_->snapshot()]() { - LOG(WARNING) << "CellDb: pre-loading all keys"; - td::uint64 total = 0; - td::Timer timer; - auto S = snapshot->for_each([&](td::Slice, td::Slice) { - ++total; - if (total % 1000000 == 0) { - LOG(INFO) << "CellDb: iterated " << total << " keys"; - } - return td::Status::OK(); - }); - if (S.is_error()) { - LOG(ERROR) << "CellDb: pre-load failed: " << S.move_as_error(); - } else { - LOG(WARNING) << "CellDb: iterated " << total << " keys in " << timer.elapsed() << "s"; - } - }, td::Timestamp::now()); + delay_action( + [snapshot = cell_db_->snapshot()]() { + LOG(WARNING) << "CellDb: pre-loading all keys"; + td::uint64 total = 0; + td::Timer timer; + auto S = snapshot->for_each([&](td::Slice, td::Slice) { + ++total; + if (total % 1000000 == 0) { + LOG(INFO) << "CellDb: iterated " << total << " keys"; + } + return td::Status::OK(); + }); + if (S.is_error()) { + LOG(ERROR) << "CellDb: pre-load failed: " << S.move_as_error(); + } else { + LOG(WARNING) << "CellDb: iterated " << total << " keys in " << timer.elapsed() << "s"; + } + }, + td::Timestamp::now()); } } void CellDbIn::load_cell(RootHash hash, td::Promise> promise) { - boc_->load_cell_async(hash.as_slice(), async_executor, std::move(promise)); + if (db_busy_) { + action_queue_.push([self = this, hash, promise = std::move(promise)](td::Result R) mutable { + R.ensure(); + self->load_cell(hash, std::move(promise)); + }); + return; + } + if (opts_->get_celldb_in_memory()) { + auto result = boc_->load_root(hash.as_slice()); + async_apply("load_cell_result", std::move(promise), std::move(result)); + return; + } + auto cell = boc_->load_cell(hash.as_slice()); + delay_action( + [cell = std::move(cell), promise = std::move(promise)]() mutable { promise.set_result(std::move(cell)); }, + td::Timestamp::now()); } void CellDbIn::store_cell(BlockIdExt block_id, td::Ref cell, td::Promise> promise) { + if (db_busy_) { + action_queue_.push( + [self = this, block_id, cell = std::move(cell), promise = std::move(promise)](td::Result R) mutable { + R.ensure(); + self->store_cell(block_id, std::move(cell), std::move(promise)); + }); + return; + } td::PerfWarningTimer timer{"storecell", 0.1}; auto key_hash = get_key_hash(block_id); auto R = get_block(key_hash); @@ -151,60 +197,126 @@ void CellDbIn::store_cell(BlockIdExt block_id, td::Ref cell, td::Promi return; } - auto empty = get_empty_key_hash(); - auto ER = get_block(empty); - ER.ensure(); - auto E = ER.move_as_ok(); - - auto PR = get_block(E.prev); - PR.ensure(); - auto P = PR.move_as_ok(); - CHECK(P.next == empty); - - DbEntry D{block_id, E.prev, empty, cell->get_hash().bits()}; - - E.prev = key_hash; - P.next = key_hash; - - if (P.is_empty()) { - E.next = key_hash; - P.prev = key_hash; - } - boc_->inc(cell); - boc_->prepare_commit().ensure(); - vm::CellStorer stor{*cell_db_.get()}; - cell_db_->begin_write_batch().ensure(); - boc_->commit(stor).ensure(); - set_block(empty, std::move(E)); - set_block(D.prev, std::move(P)); - set_block(key_hash, std::move(D)); - cell_db_->commit_write_batch().ensure(); - - boc_->set_loader(std::make_unique(cell_db_->snapshot(), on_load_callback_)).ensure(); - td::actor::send_closure(parent_, &CellDb::update_snapshot, cell_db_->snapshot()); + db_busy_ = true; + boc_->prepare_commit_async(async_executor, [=, this, SelfId = actor_id(this), timer = std::move(timer), + timer_prepare = td::Timer{}, promise = std::move(promise), + cell = std::move(cell)](td::Result Res) mutable { + Res.ensure(); + timer_prepare.pause(); + td::actor::send_lambda( + SelfId, [=, this, timer = std::move(timer), promise = std::move(promise), cell = std::move(cell)]() mutable { + TD_PERF_COUNTER(celldb_store_cell); + auto empty = get_empty_key_hash(); + auto ER = get_block(empty); + ER.ensure(); + auto E = ER.move_as_ok(); + + auto PR = get_block(E.prev); + PR.ensure(); + auto P = PR.move_as_ok(); + CHECK(P.next == empty); + + DbEntry D{block_id, E.prev, empty, cell->get_hash().bits()}; + + E.prev = key_hash; + P.next = key_hash; + + if (P.is_empty()) { + E.next = key_hash; + P.prev = key_hash; + } + td::Timer timer_write; + vm::CellStorer stor{*cell_db_}; + cell_db_->begin_write_batch().ensure(); + boc_->commit(stor).ensure(); + set_block(get_empty_key_hash(), std::move(E)); + set_block(D.prev, std::move(P)); + set_block(key_hash, std::move(D)); + cell_db_->commit_write_batch().ensure(); + timer_write.pause(); + + if (!opts_->get_celldb_in_memory()) { + boc_->set_loader(std::make_unique(cell_db_->snapshot(), on_load_callback_)).ensure(); + td::actor::send_closure(parent_, &CellDb::update_snapshot, cell_db_->snapshot()); + } - promise.set_result(boc_->load_cell(cell->get_hash().as_slice())); - if (!opts_->get_disable_rocksdb_stats()) { - cell_db_statistics_.store_cell_time_.insert(timer.elapsed() * 1e6); - } - LOG(DEBUG) << "Stored state " << block_id.to_str(); + promise.set_result(boc_->load_cell(cell->get_hash().as_slice())); + if (!opts_->get_disable_rocksdb_stats()) { + cell_db_statistics_.store_cell_time_.insert(timer.elapsed() * 1e6); + cell_db_statistics_.store_cell_prepare_time_.insert(timer_prepare.elapsed() * 1e6); + cell_db_statistics_.store_cell_write_time_.insert(timer_write.elapsed() * 1e6); + } + LOG(DEBUG) << "Stored state " << block_id.to_str(); + release_db(); + }); + }); } void CellDbIn::get_cell_db_reader(td::Promise> promise) { + if (db_busy_) { + action_queue_.push( + [self = this, promise = std::move(promise)](td::Result R) mutable { + R.ensure(); + self->get_cell_db_reader(std::move(promise)); + }); + return; + } promise.set_result(boc_->get_cell_db_reader()); } -void CellDbIn::get_last_deleted_mc_state(td::Promise promise) { - promise.set_result(last_deleted_mc_state_); -} +std::vector> CellDbIn::prepare_stats() { + TD_PERF_COUNTER(celldb_prepare_stats); + auto r_boc_stats = boc_->get_stats(); + if (r_boc_stats.is_ok()) { + cell_db_statistics_.boc_stats_ = r_boc_stats.move_as_ok(); + } + cell_db_statistics_.in_memory_load_time_ = in_memory_load_time_; + auto stats = cell_db_statistics_.prepare_stats(); + auto add_stat = [&](const auto& key, const auto& value) { stats.emplace_back(key, PSTRING() << value); }; + + add_stat("started", "true"); + auto r_mem_stat = td::mem_stat(); + auto r_total_mem_stat = td::get_total_mem_stat(); + td::uint64 celldb_size = 0; + bool ok_celldb_size = rocks_db_->GetIntProperty("rocksdb.total-sst-files-size", &celldb_size); + if (celldb_size > 0 && r_mem_stat.is_ok() && r_total_mem_stat.is_ok() && ok_celldb_size) { + auto mem_stat = r_mem_stat.move_as_ok(); + auto total_mem_stat = r_total_mem_stat.move_as_ok(); + add_stat("rss", td::format::as_size(mem_stat.resident_size_)); + add_stat("available_ram", td::format::as_size(total_mem_stat.available_ram)); + add_stat("total_ram", td::format::as_size(total_mem_stat.total_ram)); + add_stat("actual_ram_to_celldb_ratio", double(total_mem_stat.available_ram) / double(celldb_size)); + add_stat("if_restarted_ram_to_celldb_ratio", + double(total_mem_stat.available_ram + mem_stat.resident_size_ - 10 * (td::uint64(1) << 30)) / + double(celldb_size)); + add_stat("max_possible_ram_to_celldb_ratio", double(total_mem_stat.total_ram) / double(celldb_size)); + } + stats.emplace_back("last_deleted_mc_state", td::to_string(last_deleted_mc_state_)); + return stats; + // do not clear statistics, it is needed for flush_db_stats +} void CellDbIn::flush_db_stats() { if (opts_->get_disable_rocksdb_stats()) { return; } - auto stats = td::RocksDb::statistics_to_string(statistics_) + snapshot_statistics_->to_string() + - cell_db_statistics_.to_string(); + if (db_busy_) { + action_queue_.push([self = this](td::Result R) mutable { + R.ensure(); + self->flush_db_stats(); + }); + return; + } + + auto celldb_stats = prepare_stats(); + td::StringBuilder ss; + for (auto& [key, value] : celldb_stats) { + ss << "ton.celldb." << key << " " << value << "\n"; + } + + auto stats = + td::RocksDb::statistics_to_string(statistics_) + snapshot_statistics_->to_string() + ss.as_cslice().str(); auto to_file_r = td::FileFd::open(path_ + "/db_stats.txt", td::FileFd::Truncate | td::FileFd::Create | td::FileFd::Write, 0644); if (to_file_r.is_error()) { @@ -284,8 +396,18 @@ void CellDbIn::gc_cont(BlockHandle handle) { } void CellDbIn::gc_cont2(BlockHandle handle) { + if (db_busy_) { + action_queue_.push([self = this, handle = std::move(handle)](td::Result R) mutable { + R.ensure(); + self->gc_cont2(handle); + }); + return; + } + td::PerfWarningTimer timer{"gccell", 0.1}; + td::PerfWarningTimer timer_all{"gccell_all", 0.05}; + td::PerfWarningTimer timer_get_keys{"gccell_get_keys", 0.05}; auto key_hash = get_key_hash(handle->id()); auto FR = get_block(key_hash); FR.ensure(); @@ -304,31 +426,64 @@ void CellDbIn::gc_cont2(BlockHandle handle) { P.prev = P.next; N.next = N.prev; } + timer_get_keys.reset(); + td::PerfWarningTimer timer_boc{"gccell_boc", 0.05}; auto cell = boc_->load_cell(F.root_hash.as_slice()).move_as_ok(); boc_->dec(cell); - boc_->prepare_commit().ensure(); - vm::CellStorer stor{*cell_db_}; - cell_db_->begin_write_batch().ensure(); - boc_->commit(stor).ensure(); - cell_db_->erase(get_key(key_hash)).ensure(); - set_block(F.prev, std::move(P)); - set_block(F.next, std::move(N)); - cell_db_->commit_write_batch().ensure(); - alarm_timestamp() = td::Timestamp::now(); + db_busy_ = true; + boc_->prepare_commit_async( + async_executor, [this, SelfId = actor_id(this), timer_boc = std::move(timer_boc), F = std::move(F), key_hash, + P = std::move(P), N = std::move(N), cell = std::move(cell), timer = std::move(timer), + timer_all = std::move(timer_all), handle](td::Result R) mutable { + R.ensure(); + td::actor::send_lambda(SelfId, [this, timer_boc = std::move(timer_boc), F = std::move(F), key_hash, + P = std::move(P), N = std::move(N), cell = std::move(cell), + timer = std::move(timer), timer_all = std::move(timer_all), handle]() mutable { + TD_PERF_COUNTER(celldb_gc_cell); + vm::CellStorer stor{*cell_db_}; + timer_boc.reset(); + + td::PerfWarningTimer timer_write_batch{"gccell_write_batch", 0.05}; + cell_db_->begin_write_batch().ensure(); + boc_->commit(stor).ensure(); + + cell_db_->erase(get_key(key_hash)).ensure(); + set_block(F.prev, std::move(P)); + set_block(F.next, std::move(N)); + cell_db_->commit_write_batch().ensure(); + alarm_timestamp() = td::Timestamp::now(); + timer_write_batch.reset(); + + td::PerfWarningTimer timer_free_cells{"gccell_free_cells", 0.05}; + auto before = td::ref_get_delete_count(); + cell = {}; + auto after = td::ref_get_delete_count(); + if (timer_free_cells.elapsed() > 0.04) { + LOG(ERROR) << "deleted " << after - before << " cells"; + } + timer_free_cells.reset(); - boc_->set_loader(std::make_unique(cell_db_->snapshot(), on_load_callback_)).ensure(); - td::actor::send_closure(parent_, &CellDb::update_snapshot, cell_db_->snapshot()); + td::PerfWarningTimer timer_finish{"gccell_finish", 0.05}; + if (!opts_->get_celldb_in_memory()) { + boc_->set_loader(std::make_unique(cell_db_->snapshot(), on_load_callback_)).ensure(); + td::actor::send_closure(parent_, &CellDb::update_snapshot, cell_db_->snapshot()); + } - DCHECK(get_block(key_hash).is_error()); - if (!opts_->get_disable_rocksdb_stats()) { - cell_db_statistics_.gc_cell_time_.insert(timer.elapsed() * 1e6); - } - if (handle->id().is_masterchain()) { - last_deleted_mc_state_ = handle->id().seqno(); - } - LOG(DEBUG) << "Deleted state " << handle->id().to_str(); + DCHECK(get_block(key_hash).is_error()); + if (!opts_->get_disable_rocksdb_stats()) { + cell_db_statistics_.gc_cell_time_.insert(timer.elapsed() * 1e6); + } + if (handle->id().is_masterchain()) { + last_deleted_mc_state_ = handle->id().seqno(); + } + LOG(DEBUG) << "Deleted state " << handle->id().to_str(); + timer_finish.reset(); + timer_all.reset(); + release_db(); + }); + }); } void CellDbIn::skip_gc() { @@ -388,6 +543,13 @@ void CellDbIn::migrate_cell(td::Bits256 hash) { void CellDbIn::migrate_cells() { migrate_after_ = td::Timestamp::never(); + if (db_busy_) { + action_queue_.push([self = this](td::Result R) mutable { + R.ensure(); + self->migrate_cells(); + }); + return; + } if (cells_to_migrate_.empty()) { migration_active_ = false; return; @@ -401,7 +563,7 @@ void CellDbIn::migrate_cells() { boc_->set_loader(std::make_unique(*loader)).ensure(); cell_db_->begin_write_batch().ensure(); td::uint32 checked = 0, migrated = 0; - for (auto it = cells_to_migrate_.begin(); it != cells_to_migrate_.end() && checked < 128; ) { + for (auto it = cells_to_migrate_.begin(); it != cells_to_migrate_.end() && checked < 128;) { ++checked; td::Bits256 hash = *it; it = cells_to_migrate_.erase(it); @@ -438,7 +600,32 @@ void CellDbIn::migrate_cells() { } } +void CellDb::prepare_stats(td::Promise>> promise) { + promise.set_value(decltype(prepared_stats_)(prepared_stats_)); +} + +void CellDb::update_stats(td::Result>> r_stats) { + if (r_stats.is_error()) { + LOG(ERROR) << "error updating stats: " << r_stats.error(); + } else { + prepared_stats_ = r_stats.move_as_ok(); + } + alarm_timestamp() = td::Timestamp::in(2.0); +} + +void CellDb::alarm() { + send_closure(cell_db_, &CellDbIn::prepare_stats, td::promise_send_closure(actor_id(this), &CellDb::update_stats)); +} + void CellDb::load_cell(RootHash hash, td::Promise> promise) { + if (in_memory_boc_) { + auto result = in_memory_boc_->load_root_thread_safe(hash.as_slice()); + if (result.is_ok()) { + return async_apply("load_cell_result", std::move(promise), std::move(result)); + } else { + LOG(ERROR) << "load_root_thread_safe failed - this is suspicious"; + } + } if (!started_) { td::actor::send_closure(cell_db_, &CellDbIn::load_cell, hash, std::move(promise)); } else { @@ -462,10 +649,6 @@ void CellDb::get_cell_db_reader(td::Promise> p td::actor::send_closure(cell_db_, &CellDbIn::get_cell_db_reader, std::move(promise)); } -void CellDb::get_last_deleted_mc_state(td::Promise promise) { - td::actor::send_closure(cell_db_, &CellDbIn::get_last_deleted_mc_state, std::move(promise)); -} - void CellDb::start_up() { CellDbBase::start_up(); boc_ = vm::DynamicBagOfCellsDb::create(); @@ -496,12 +679,26 @@ td::BufferSlice CellDbIn::DbEntry::release() { return create_serialize_tl_object(create_tl_block_id(block_id), prev, next, root_hash); } -std::string CellDbIn::CellDbStatistics::to_string() { - td::StringBuilder ss; - ss << "ton.celldb.store_cell.micros " << store_cell_time_.to_string() << "\n"; - ss << "ton.celldb.gc_cell.micros " << gc_cell_time_.to_string() << "\n"; - ss << "ton.celldb.total_time.micros : " << (td::Timestamp::now().at() - stats_start_time_.at()) * 1e6 << "\n"; - return ss.as_cslice().str(); +std::vector> CellDbIn::CellDbStatistics::prepare_stats() { + std::vector> stats; + stats.emplace_back("store_cell.micros", PSTRING() << store_cell_time_.to_string()); + stats.emplace_back("store_cell.prepare.micros", PSTRING() << store_cell_prepare_time_.to_string()); + stats.emplace_back("store_cell.write.micros", PSTRING() << store_cell_write_time_.to_string()); + stats.emplace_back("gc_cell.micros", PSTRING() << gc_cell_time_.to_string()); + stats.emplace_back("total_time.micros", PSTRING() << (td::Timestamp::now().at() - stats_start_time_.at()) * 1e6); + stats.emplace_back("in_memory", PSTRING() << bool(in_memory_load_time_)); + if (in_memory_load_time_) { + stats.emplace_back("in_memory_load_time", PSTRING() << in_memory_load_time_.value()); + } + if (boc_stats_) { + stats.emplace_back("cells_count", PSTRING() << boc_stats_->cells_total_count); + stats.emplace_back("cells_size", PSTRING() << boc_stats_->cells_total_size); + stats.emplace_back("roots_count", PSTRING() << boc_stats_->roots_total_count); + for (auto& [key, value] : boc_stats_->custom_stats) { + stats.emplace_back(key, value); + } + } + return stats; } } // namespace validator diff --git a/validator/db/celldb.hpp b/validator/db/celldb.hpp index 335d8a08e..5639b9748 100644 --- a/validator/db/celldb.hpp +++ b/validator/db/celldb.hpp @@ -29,9 +29,13 @@ #include "db-utils.h" #include "td/db/RocksDb.h" +#include +#include + namespace rocksdb { class Statistics; -} +class DB; +} // namespace rocksdb namespace ton { @@ -58,10 +62,10 @@ class CellDbIn : public CellDbBase { public: using KeyHash = td::Bits256; + std::vector> prepare_stats(); void load_cell(RootHash hash, td::Promise> promise); void store_cell(BlockIdExt block_id, td::Ref cell, td::Promise> promise); void get_cell_db_reader(td::Promise> promise); - void get_last_deleted_mc_state(td::Promise promise); void migrate_cell(td::Bits256 hash); @@ -111,13 +115,15 @@ class CellDbIn : public CellDbBase { std::string path_; td::Ref opts_; - std::unique_ptr boc_; + std::shared_ptr boc_; std::shared_ptr cell_db_; + std::shared_ptr rocks_db_; std::function on_load_callback_; std::set cells_to_migrate_; td::Timestamp migrate_after_ = td::Timestamp::never(); bool migration_active_ = false; + std::optional in_memory_load_time_; struct MigrationStats { td::Timer start_; @@ -131,10 +137,14 @@ class CellDbIn : public CellDbBase { struct CellDbStatistics { PercentileStats store_cell_time_; + PercentileStats store_cell_prepare_time_; + PercentileStats store_cell_write_time_; PercentileStats gc_cell_time_; td::Timestamp stats_start_time_ = td::Timestamp::now(); + std::optional in_memory_load_time_; + std::optional boc_stats_; - std::string to_string(); + std::vector> prepare_stats(); void clear() { *this = CellDbStatistics{}; } @@ -146,6 +156,18 @@ class CellDbIn : public CellDbBase { td::Timestamp statistics_flush_at_ = td::Timestamp::never(); BlockSeqno last_deleted_mc_state_ = 0; + bool db_busy_ = false; + std::queue> action_queue_; + + void release_db() { + db_busy_ = false; + while (!db_busy_ && !action_queue_.empty()) { + auto action = std::move(action_queue_.front()); + action_queue_.pop(); + action.set_value(td::Unit()); + } + } + public: class MigrationProxy : public td::actor::Actor { public: @@ -162,14 +184,26 @@ class CellDbIn : public CellDbBase { class CellDb : public CellDbBase { public: + void prepare_stats(td::Promise>> promise); void load_cell(RootHash hash, td::Promise> promise); void store_cell(BlockIdExt block_id, td::Ref cell, td::Promise> promise); void update_snapshot(std::unique_ptr snapshot) { + CHECK(!opts_->get_celldb_in_memory()); + if (!started_) { + alarm(); + } started_ = true; boc_->set_loader(std::make_unique(std::move(snapshot), on_load_callback_)).ensure(); } + void set_in_memory_boc(std::shared_ptr in_memory_boc) { + CHECK(opts_->get_celldb_in_memory()); + if (!started_) { + alarm(); + } + started_ = true; + in_memory_boc_ = std::move(in_memory_boc); + } void get_cell_db_reader(td::Promise> promise); - void get_last_deleted_mc_state(td::Promise promise); CellDb(td::actor::ActorId root_db, std::string path, td::Ref opts) : root_db_(root_db), path_(path), opts_(opts) { @@ -185,9 +219,14 @@ class CellDb : public CellDbBase { td::actor::ActorOwn cell_db_; std::unique_ptr boc_; + std::shared_ptr in_memory_boc_; bool started_ = false; + std::vector> prepared_stats_{{"started", "false"}}; std::function on_load_callback_; + + void update_stats(td::Result>> stats); + void alarm() override; }; } // namespace validator diff --git a/validator/db/fileref.cpp b/validator/db/fileref.cpp index 2e2a3b6b6..feefd1f9c 100644 --- a/validator/db/fileref.cpp +++ b/validator/db/fileref.cpp @@ -79,14 +79,14 @@ std::string Block::filename() const { std::string Block::filename_short() const { char s[33]; - sprintf(s, "%llx", static_cast(block_id.id.shard)); + snprintf(s, sizeof(s), "%llx", static_cast(block_id.id.shard)); return PSTRING() << "block_" << block_id.id.workchain << "_" << s << "_" << block_id.id.seqno << "_" << hash().to_hex(); } std::string BlockShort::filename_short() const { char s[33]; - sprintf(s, "%llx", static_cast(block_id.shard)); + snprintf(s, sizeof(s), "%llx", static_cast(block_id.shard)); return PSTRING() << "block_" << block_id.workchain << "_" << s << "_" << block_id.seqno << "_" << hash().to_hex(); } @@ -116,14 +116,14 @@ std::string PersistentState::filename() const { std::string PersistentState::filename_short() const { char s[33]; - sprintf(s, "%llx", static_cast(block_id.id.shard)); + snprintf(s, sizeof(s), "%llx", static_cast(block_id.id.shard)); return PSTRING() << "state_" << masterchain_block_id.seqno() << "_" << block_id.id.workchain << "_" << s << "_" << hash().to_hex(); } std::string PersistentStateShort::filename_short() const { char s[33]; - sprintf(s, "%llx", static_cast(shard_id.shard)); + snprintf(s, sizeof(s), "%llx", static_cast(shard_id.shard)); return PSTRING() << "state_" << masterchain_seqno << "_" << shard_id.workchain << "_" << s << "_" << hash().to_hex(); } @@ -137,14 +137,14 @@ std::string Proof::filename() const { std::string Proof::filename_short() const { char s[33]; - sprintf(s, "%llx", static_cast(block_id.id.shard)); + snprintf(s, sizeof(s), "%llx", static_cast(block_id.id.shard)); return PSTRING() << "proof_" << block_id.id.workchain << "_" << s << "_" << block_id.id.seqno << "_" << hash().to_hex(); } std::string ProofShort::filename_short() const { char s[33]; - sprintf(s, "%llx", static_cast(block_id.shard)); + snprintf(s, sizeof(s), "%llx", static_cast(block_id.shard)); return PSTRING() << "proof_" << block_id.workchain << "_" << s << "_" << block_id.seqno << "_" << hash().to_hex(); } @@ -158,14 +158,14 @@ std::string ProofLink::filename() const { std::string ProofLink::filename_short() const { char s[33]; - sprintf(s, "%llx", static_cast(block_id.id.shard)); + snprintf(s, sizeof(s), "%llx", static_cast(block_id.id.shard)); return PSTRING() << "prooflink_" << block_id.id.workchain << "_" << s << "_" << block_id.id.seqno << "_" << hash().to_hex(); } std::string ProofLinkShort::filename_short() const { char s[33]; - sprintf(s, "%llx", static_cast(block_id.shard)); + snprintf(s, sizeof(s), "%llx", static_cast(block_id.shard)); return PSTRING() << "prooflink_" << block_id.workchain << "_" << s << "_" << block_id.seqno << "_" << hash().to_hex(); } @@ -179,14 +179,14 @@ std::string Signatures::filename() const { std::string Signatures::filename_short() const { char s[33]; - sprintf(s, "%llx", static_cast(block_id.id.shard)); + snprintf(s, sizeof(s), "%llx", static_cast(block_id.id.shard)); return PSTRING() << "signatures_" << block_id.id.workchain << "_" << s << "_" << block_id.id.seqno << "_" << hash().to_hex(); } std::string SignaturesShort::filename_short() const { char s[33]; - sprintf(s, "%llx", static_cast(block_id.shard)); + snprintf(s, sizeof(s), "%llx", static_cast(block_id.shard)); return PSTRING() << "signatures_" << block_id.workchain << "_" << s << "_" << block_id.seqno << "_" << hash().to_hex(); } @@ -202,17 +202,39 @@ std::string Candidate::filename() const { std::string Candidate::filename_short() const { char s[33]; - sprintf(s, "%llx", static_cast(block_id.id.shard)); + snprintf(s, sizeof(s), "%llx", static_cast(block_id.id.shard)); return PSTRING() << "candidate_" << block_id.id.workchain << "_" << s << "_" << block_id.id.seqno << "_" << hash().to_hex(); } std::string CandidateShort::filename_short() const { char s[33]; - sprintf(s, "%llx", static_cast(block_id.shard)); + snprintf(s, sizeof(s), "%llx", static_cast(block_id.shard)); return PSTRING() << "candidate_" << block_id.workchain << "_" << s << "_" << block_id.seqno << "_" << hash().to_hex(); } +CandidateRefShort CandidateRef::shortref() const { + return CandidateRefShort{block_id.id, hash()}; +} + +std::string CandidateRef::filename() const { + return PSTRING() << "candidateref_" << block_id.to_str(); +} + +std::string CandidateRef::filename_short() const { + char s[33]; + snprintf(s, sizeof(s), "%llx", static_cast(block_id.id.shard)); + return PSTRING() << "candidateref_" << block_id.id.workchain << "_" << s << "_" << block_id.id.seqno << "_" + << hash().to_hex(); +} + +std::string CandidateRefShort::filename_short() const { + char s[33]; + snprintf(s, sizeof(s), "%llx", static_cast(block_id.shard)); + return PSTRING() << "candidateref_" << block_id.workchain << "_" << s << "_" << block_id.seqno << "_" + << hash().to_hex(); +} + BlockInfoShort BlockInfo::shortref() const { return BlockInfoShort{block_id.id, hash()}; } @@ -223,14 +245,14 @@ std::string BlockInfo::filename() const { std::string BlockInfo::filename_short() const { char s[33]; - sprintf(s, "%llx", static_cast(block_id.id.shard)); + snprintf(s, sizeof(s), "%llx", static_cast(block_id.id.shard)); return PSTRING() << "info_" << block_id.id.workchain << "_" << s << "_" << block_id.id.seqno << "_" << hash().to_hex(); } std::string BlockInfoShort::filename_short() const { char s[33]; - sprintf(s, "%llx", static_cast(block_id.shard)); + snprintf(s, sizeof(s), "%llx", static_cast(block_id.shard)); return PSTRING() << "info_" << block_id.workchain << "_" << s << "_" << block_id.seqno << "_" << hash().to_hex(); } @@ -259,6 +281,9 @@ FileReference::FileReference(tl_object_ptr key) { ref_ = fileref::Candidate{PublicKey{key.id_->source_}, create_block_id(key.id_->id_), key.id_->collated_data_file_hash_}; }, + [&](const ton_api::db_filedb_key_candidateRef& key) { + ref_ = fileref::CandidateRef{create_block_id(key.id_)}; + }, [&](const ton_api::db_filedb_key_blockInfo& key) { ref_ = fileref::BlockInfo{create_block_id(key.block_id_)}; })); diff --git a/validator/db/fileref.hpp b/validator/db/fileref.hpp index 14424a653..6710cc063 100644 --- a/validator/db/fileref.hpp +++ b/validator/db/fileref.hpp @@ -278,6 +278,38 @@ class Candidate { FileHash collated_data_file_hash; }; +class CandidateRefShort { + public: + FileHash hash() const { + return hashv; + } + ShardIdFull shard() const { + return block_id.shard_full(); + } + std::string filename_short() const; + + BlockId block_id; + FileHash hashv; +}; + +class CandidateRef { + public: + tl_object_ptr tl() const { + return create_tl_object(create_tl_block_id(block_id)); + } + FileHash hash() const { + return create_hash_tl_object(create_tl_block_id(block_id)); + } + ShardIdFull shard() const { + return block_id.shard_full(); + } + CandidateRefShort shortref() const; + std::string filename() const; + std::string filename_short() const; + + BlockIdExt block_id; +}; + class BlockInfoShort { public: FileHash hash() const { @@ -316,7 +348,7 @@ class FileReferenceShort { private: td::Variant + fileref::CandidateShort, fileref::CandidateRefShort, fileref::BlockInfoShort> ref_; public: @@ -340,7 +372,8 @@ class FileReferenceShort { class FileReference { private: td::Variant + fileref::Proof, fileref::ProofLink, fileref::Signatures, fileref::Candidate, fileref::CandidateRef, + fileref::BlockInfo> ref_; public: diff --git a/validator/db/rootdb.cpp b/validator/db/rootdb.cpp index 3071f565d..bb5d767fd 100644 --- a/validator/db/rootdb.cpp +++ b/validator/db/rootdb.cpp @@ -177,21 +177,21 @@ void RootDb::get_block_proof_link(ConstBlockHandle handle, td::Promise promise) { + auto source = PublicKey{pubkeys::Ed25519{candidate.pubkey.as_bits256()}}; auto obj = create_serialize_tl_object( - PublicKey{pubkeys::Ed25519{candidate.pubkey.as_bits256()}}.tl(), create_tl_block_id(candidate.id), - std::move(candidate.data), std::move(candidate.collated_data)); - - auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { - if (R.is_error()) { - promise.set_error(R.move_as_error()); - } else { - promise.set_value(td::Unit()); - } - }); + source.tl(), create_tl_block_id(candidate.id), std::move(candidate.data), std::move(candidate.collated_data)); + auto P = td::PromiseCreator::lambda( + [archive_db = archive_db_.get(), promise = std::move(promise), block_id = candidate.id, source, + collated_file_hash = candidate.collated_file_hash](td::Result R) mutable { + TRY_RESULT_PROMISE(promise, _, std::move(R)); + td::actor::send_closure(archive_db, &ArchiveManager::add_temp_file_short, fileref::CandidateRef{block_id}, + create_serialize_tl_object( + source.tl(), create_tl_block_id(block_id), collated_file_hash), + std::move(promise)); + }); td::actor::send_closure(archive_db_, &ArchiveManager::add_temp_file_short, - fileref::Candidate{PublicKey{pubkeys::Ed25519{candidate.pubkey.as_bits256()}}, candidate.id, - candidate.collated_file_hash}, - std::move(obj), std::move(P)); + fileref::Candidate{source, candidate.id, candidate.collated_file_hash}, std::move(obj), + std::move(P)); } void RootDb::get_block_candidate(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash, @@ -215,6 +215,17 @@ void RootDb::get_block_candidate(PublicKey source, BlockIdExt id, FileHash colla fileref::Candidate{source, id, collated_data_file_hash}, std::move(P)); } +void RootDb::get_block_candidate_by_block_id(BlockIdExt id, td::Promise promise) { + td::actor::send_closure( + archive_db_, &ArchiveManager::get_temp_file_short, fileref::CandidateRef{id}, + [SelfId = actor_id(this), promise = std::move(promise)](td::Result R) mutable { + TRY_RESULT_PROMISE(promise, data, std::move(R)); + TRY_RESULT_PROMISE(promise, f, fetch_tl_object(data, true)); + td::actor::send_closure(SelfId, &RootDb::get_block_candidate, PublicKey{f->source_}, create_block_id(f->id_), + f->collated_data_file_hash_, std::move(promise)); + }); +} + void RootDb::store_block_state(BlockHandle handle, td::Ref state, td::Promise> promise) { if (handle->moved_to_archive()) { @@ -274,10 +285,6 @@ void RootDb::get_cell_db_reader(td::Promise> p td::actor::send_closure(cell_db_, &CellDb::get_cell_db_reader, std::move(promise)); } -void RootDb::get_last_deleted_mc_state(td::Promise promise) { - td::actor::send_closure(cell_db_, &CellDb::get_last_deleted_mc_state, std::move(promise)); -} - void RootDb::store_persistent_state_file(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::BufferSlice state, td::Promise promise) { td::actor::send_closure(archive_db_, &ArchiveManager::add_persistent_state, block_id, masterchain_block_id, @@ -428,6 +435,7 @@ void RootDb::allow_block_gc(BlockIdExt block_id, td::Promise promise) { void RootDb::prepare_stats(td::Promise>> promise) { auto merger = StatsMerger::create(std::move(promise)); + td::actor::send_closure(cell_db_, &CellDb::prepare_stats, merger.make_promise("celldb.")); } void RootDb::truncate(BlockSeqno seqno, ConstBlockHandle handle, td::Promise promise) { diff --git a/validator/db/rootdb.hpp b/validator/db/rootdb.hpp index 061e9add8..755ff2578 100644 --- a/validator/db/rootdb.hpp +++ b/validator/db/rootdb.hpp @@ -58,12 +58,12 @@ class RootDb : public Db { void store_block_candidate(BlockCandidate candidate, td::Promise promise) override; void get_block_candidate(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash, td::Promise promise) override; + void get_block_candidate_by_block_id(BlockIdExt id, td::Promise promise) override; void store_block_state(BlockHandle handle, td::Ref state, td::Promise> promise) override; void get_block_state(ConstBlockHandle handle, td::Promise> promise) override; void get_cell_db_reader(td::Promise> promise) override; - void get_last_deleted_mc_state(td::Promise promise) override; void store_block_handle(BlockHandle handle, td::Promise promise) override; void get_block_handle(BlockIdExt id, td::Promise promise) override; diff --git a/validator/db/statedb.hpp b/validator/db/statedb.hpp index 75382d61c..a7a004522 100644 --- a/validator/db/statedb.hpp +++ b/validator/db/statedb.hpp @@ -50,9 +50,6 @@ class StateDb : public td::actor::Actor { void update_hardforks(std::vector blocks, td::Promise promise); void get_hardforks(td::Promise> promise); - void update_db_version(td::uint32 version, td::Promise promise); - void get_db_version(td::Promise promise); - StateDb(td::actor::ActorId root_db, std::string path); void start_up() override; diff --git a/validator/downloaders/download-state.cpp b/validator/downloaders/download-state.cpp index ccce8f772..32978ea5b 100644 --- a/validator/downloaders/download-state.cpp +++ b/validator/downloaders/download-state.cpp @@ -50,7 +50,16 @@ void DownloadShardState::start_up() { void DownloadShardState::got_block_handle(BlockHandle handle) { handle_ = std::move(handle); - download_state(); + if (handle_->received_state()) { + LOG(WARNING) << "shard state " << block_id_.to_str() << " already stored in db"; + td::actor::send_closure(manager_, &ValidatorManagerInterface::get_shard_state_from_db, handle_, + [SelfId = actor_id(this)](td::Result> R) { + R.ensure(); + td::actor::send_closure(SelfId, &DownloadShardState::written_shard_state, R.move_as_ok()); + }); + } else { + download_state(); + } } void DownloadShardState::retry() { @@ -165,6 +174,7 @@ void DownloadShardState::downloaded_shard_state(td::BufferSlice data) { } void DownloadShardState::checked_shard_state() { + LOG(WARNING) << "checked shard state " << block_id_.to_str(); auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { R.ensure(); td::actor::send_closure(SelfId, &DownloadShardState::written_shard_state_file); @@ -179,6 +189,7 @@ void DownloadShardState::checked_shard_state() { } void DownloadShardState::written_shard_state_file() { + LOG(WARNING) << "written shard state file " << block_id_.to_str(); auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { R.ensure(); td::actor::send_closure(SelfId, &DownloadShardState::written_shard_state, R.move_as_ok()); @@ -207,6 +218,7 @@ void DownloadShardState::written_shard_state(td::Ref state) { } void DownloadShardState::written_block_handle() { + LOG(WARNING) << "finished downloading and storing shard state " << block_id_.to_str(); finish_query(); } diff --git a/validator/downloaders/wait-block-state.cpp b/validator/downloaders/wait-block-state.cpp index 0ae82beaa..f8d2cdcbe 100644 --- a/validator/downloaders/wait-block-state.cpp +++ b/validator/downloaders/wait-block-state.cpp @@ -230,6 +230,7 @@ void WaitBlockState::got_block_data(td::Ref data) { } void WaitBlockState::apply() { + TD_PERF_COUNTER(apply_block_to_state); td::PerfWarningTimer t{"applyblocktostate", 0.1}; auto S = prev_state_.write().apply_block(handle_->id(), block_); if (S.is_error()) { diff --git a/validator/fabric.h b/validator/fabric.h index 949a6c9ff..2c39acebe 100644 --- a/validator/fabric.h +++ b/validator/fabric.h @@ -26,6 +26,8 @@ namespace ton { namespace validator { +enum CollateMode { skip_store_candidate = 1 }; + td::actor::ActorOwn create_db_actor(td::actor::ActorId manager, std::string db_root_, td::Ref opts); td::actor::ActorOwn create_liteserver_cache_actor(td::actor::ActorId manager, @@ -54,7 +56,7 @@ void run_check_external_message(td::Ref message, td::actor::ActorId< void run_accept_block_query(BlockIdExt id, td::Ref data, std::vector prev, td::Ref validator_set, td::Ref signatures, - td::Ref approve_signatures, bool send_broadcast, + td::Ref approve_signatures, int send_broadcast_mode, td::actor::ActorId manager, td::Promise promise); void run_fake_accept_block_query(BlockIdExt id, td::Ref data, std::vector prev, td::Ref validator_set, td::actor::ActorId manager, @@ -74,14 +76,15 @@ void run_check_proof_query(BlockIdExt id, td::Ref proof, td::actor::Actor td::Ref rel_key_block_proof, bool skip_check_signatures = false); void run_check_proof_link_query(BlockIdExt id, td::Ref proof, td::actor::ActorId manager, td::Timestamp timeout, td::Promise promise); -void run_validate_query(ShardIdFull shard, UnixTime min_ts, BlockIdExt min_masterchain_block_id, - std::vector prev, BlockCandidate candidate, td::Ref validator_set, +void run_validate_query(ShardIdFull shard, BlockIdExt min_masterchain_block_id, std::vector prev, + BlockCandidate candidate, td::Ref validator_set, td::actor::ActorId manager, td::Timestamp timeout, td::Promise promise, bool is_fake = false); -void run_collate_query(ShardIdFull shard, td::uint32 min_ts, const BlockIdExt& min_masterchain_block_id, - std::vector prev, Ed25519_PublicKey local_id, td::Ref validator_set, +void run_collate_query(ShardIdFull shard, const BlockIdExt& min_masterchain_block_id, std::vector prev, + Ed25519_PublicKey creator, td::Ref validator_set, td::Ref collator_opts, td::actor::ActorId manager, - td::Timestamp timeout, td::Promise promise); + td::Timestamp timeout, td::Promise promise, + td::CancellationToken cancellation_token, unsigned mode, int attempt_idx = 0); void run_collate_hardfork(ShardIdFull shard, const BlockIdExt& min_masterchain_block_id, std::vector prev, td::actor::ActorId manager, td::Timestamp timeout, td::Promise promise); diff --git a/validator/full-node.cpp b/validator/full-node.cpp index a72be3ff4..bb6da2c8b 100644 --- a/validator/full-node.cpp +++ b/validator/full-node.cpp @@ -261,21 +261,22 @@ void FullNodeImpl::send_block_candidate(BlockIdExt block_id, CatchainSeqno cc_se } } -void FullNodeImpl::send_broadcast(BlockBroadcast broadcast, bool custom_overlays_only) { - send_block_broadcast_to_custom_overlays(broadcast); - if (custom_overlays_only) { - return; +void FullNodeImpl::send_broadcast(BlockBroadcast broadcast, int mode) { + if (mode & broadcast_mode_custom) { + send_block_broadcast_to_custom_overlays(broadcast); } auto shard = get_shard(ShardIdFull{masterchainId}); if (shard.empty()) { VLOG(FULL_NODE_WARNING) << "dropping OUT broadcast to unknown shard"; return; } - if (broadcast.block_id.is_masterchain() && !private_block_overlays_.empty()) { + if (!private_block_overlays_.empty() && (mode & broadcast_mode_private_block)) { td::actor::send_closure(private_block_overlays_.begin()->second, &FullNodePrivateBlockOverlay::send_broadcast, broadcast.clone()); } - td::actor::send_closure(shard, &FullNodeShard::send_broadcast, std::move(broadcast)); + if (mode & broadcast_mode_public) { + td::actor::send_closure(shard, &FullNodeShard::send_broadcast, std::move(broadcast)); + } } void FullNodeImpl::download_block(BlockIdExt id, td::uint32 priority, td::Timestamp timeout, @@ -496,8 +497,8 @@ void FullNodeImpl::start_up() { td::actor::send_closure(id_, &FullNodeImpl::send_block_candidate, block_id, cc_seqno, validator_set_hash, std::move(data)); } - void send_broadcast(BlockBroadcast broadcast, bool custom_overlays_only) override { - td::actor::send_closure(id_, &FullNodeImpl::send_broadcast, std::move(broadcast), custom_overlays_only); + void send_broadcast(BlockBroadcast broadcast, int mode) override { + td::actor::send_closure(id_, &FullNodeImpl::send_broadcast, std::move(broadcast), mode); } void download_block(BlockIdExt id, td::uint32 priority, td::Timestamp timeout, td::Promise promise) override { diff --git a/validator/full-node.h b/validator/full-node.h index c3719f678..621cdac0d 100644 --- a/validator/full-node.h +++ b/validator/full-node.h @@ -99,6 +99,7 @@ class FullNode : public td::actor::Actor { static constexpr td::uint64 max_state_size() { return 4ull << 30; } + enum { broadcast_mode_public = 1, broadcast_mode_private_block = 2, broadcast_mode_custom = 4 }; static td::actor::ActorOwn create(ton::PublicKeyHash local_id, adnl::AdnlNodeIdShort adnl_id, FileHash zero_state_file_hash, FullNodeConfig config, diff --git a/validator/full-node.hpp b/validator/full-node.hpp index 3dfa17fdd..584be5ee2 100644 --- a/validator/full-node.hpp +++ b/validator/full-node.hpp @@ -68,7 +68,7 @@ class FullNodeImpl : public FullNode { void send_shard_block_info(BlockIdExt block_id, CatchainSeqno cc_seqnp, td::BufferSlice data); void send_block_candidate(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, td::BufferSlice data); - void send_broadcast(BlockBroadcast broadcast, bool custom_overlays_only); + void send_broadcast(BlockBroadcast broadcast, int mode); void download_block(BlockIdExt id, td::uint32 priority, td::Timestamp timeout, td::Promise promise); void download_zero_state(BlockIdExt id, td::uint32 priority, td::Timestamp timeout, td::Promise promise); diff --git a/validator/impl/CMakeLists.txt b/validator/impl/CMakeLists.txt index 42dded311..9df6725d6 100644 --- a/validator/impl/CMakeLists.txt +++ b/validator/impl/CMakeLists.txt @@ -27,7 +27,6 @@ set(TON_VALIDATOR_SOURCE block.hpp candidates-buffer.hpp check-proof.hpp - collate-query-impl.h collator-impl.h collator.h config.hpp diff --git a/validator/impl/accept-block.cpp b/validator/impl/accept-block.cpp index 3da1167aa..a9dd7fe27 100644 --- a/validator/impl/accept-block.cpp +++ b/validator/impl/accept-block.cpp @@ -41,7 +41,7 @@ using namespace std::literals::string_literals; AcceptBlockQuery::AcceptBlockQuery(BlockIdExt id, td::Ref data, std::vector prev, td::Ref validator_set, td::Ref signatures, - td::Ref approve_signatures, bool send_broadcast, + td::Ref approve_signatures, int send_broadcast_mode, td::actor::ActorId manager, td::Promise promise) : id_(id) , data_(std::move(data)) @@ -51,7 +51,7 @@ AcceptBlockQuery::AcceptBlockQuery(BlockIdExt id, td::Ref data, std:: , approve_signatures_(std::move(approve_signatures)) , is_fake_(false) , is_fork_(false) - , send_broadcast_(send_broadcast) + , send_broadcast_mode_(send_broadcast_mode) , manager_(manager) , promise_(std::move(promise)) , perf_timer_("acceptblock", 0.1, [manager](double duration) { @@ -72,7 +72,6 @@ AcceptBlockQuery::AcceptBlockQuery(AcceptBlockQuery::IsFake fake, BlockIdExt id, , validator_set_(std::move(validator_set)) , is_fake_(true) , is_fork_(false) - , send_broadcast_(false) , manager_(manager) , promise_(std::move(promise)) , perf_timer_("acceptblock", 0.1, [manager](double duration) { @@ -90,7 +89,6 @@ AcceptBlockQuery::AcceptBlockQuery(ForceFork ffork, BlockIdExt id, td::Refinited_proof() && handle_->is_applied() && handle_->inited_is_key_block() : handle_->inited_proof_link())) { finish_query(); + return; + } + if (data_.is_null()) { + td::actor::send_closure(manager_, &ValidatorManager::get_candidate_data_by_block_id_from_db, id_, [SelfId = actor_id(this)](td::Result R) { + if (R.is_ok()) { + td::actor::send_closure(SelfId, &AcceptBlockQuery::got_block_candidate_data, R.move_as_ok()); + } else { + td::actor::send_closure(SelfId, &AcceptBlockQuery::got_block_handle_cont); + } + }); + } else { + got_block_handle_cont(); + } +} + +void AcceptBlockQuery::got_block_candidate_data(td::BufferSlice data) { + auto r_block = create_block(id_, std::move(data)); + if (r_block.is_error()) { + fatal_error("invalid block candidate data in db: " + r_block.error().to_string()); + return; + } + data_ = r_block.move_as_ok(); + VLOG(VALIDATOR_DEBUG) << "got block candidate data from db"; + if (data_.not_null() && !precheck_header()) { + fatal_error("invalid block header in AcceptBlock"); return; } + got_block_handle_cont(); +} + +void AcceptBlockQuery::got_block_handle_cont() { if (data_.not_null() && !handle_->received()) { td::actor::send_closure( manager_, &ValidatorManager::set_block_data, handle_, data_, [SelfId = actor_id(this)](td::Result R) { @@ -899,6 +926,10 @@ void AcceptBlockQuery::written_block_info_2() { } void AcceptBlockQuery::applied() { + if (send_broadcast_mode_ == 0) { + finish_query(); + return; + } BlockBroadcast b; b.data = data_->data(); b.block_id = id_; @@ -918,8 +949,7 @@ void AcceptBlockQuery::applied() { } // do not wait for answer - td::actor::send_closure_later(manager_, &ValidatorManager::send_block_broadcast, std::move(b), - /* custom_overlays_only = */ !send_broadcast_); + td::actor::send_closure_later(manager_, &ValidatorManager::send_block_broadcast, std::move(b), send_broadcast_mode_); finish_query(); } diff --git a/validator/impl/accept-block.hpp b/validator/impl/accept-block.hpp index 2600b2581..d1c0baa6f 100644 --- a/validator/impl/accept-block.hpp +++ b/validator/impl/accept-block.hpp @@ -50,7 +50,7 @@ class AcceptBlockQuery : public td::actor::Actor { struct ForceFork {}; AcceptBlockQuery(BlockIdExt id, td::Ref data, std::vector prev, td::Ref validator_set, td::Ref signatures, - td::Ref approve_signatures, bool send_broadcast, + td::Ref approve_signatures, int send_broadcast_mode, td::actor::ActorId manager, td::Promise promise); AcceptBlockQuery(IsFake fake, BlockIdExt id, td::Ref data, std::vector prev, td::Ref validator_set, td::actor::ActorId manager, @@ -71,6 +71,8 @@ class AcceptBlockQuery : public td::actor::Actor { void written_block_data(); void written_block_signatures(); void got_block_handle(BlockHandle handle); + void got_block_candidate_data(td::BufferSlice data); + void got_block_handle_cont(); void written_block_info(); void got_block_data(td::Ref data); void got_prev_state(td::Ref state); @@ -97,7 +99,7 @@ class AcceptBlockQuery : public td::actor::Actor { Ref approve_signatures_; bool is_fake_; bool is_fork_; - bool send_broadcast_; + int send_broadcast_mode_{0}; bool ancestors_split_{false}, is_key_block_{false}; td::Timestamp timeout_ = td::Timestamp::in(600.0); td::actor::ActorId manager_; diff --git a/validator/impl/collate-query-impl.h b/validator/impl/collate-query-impl.h deleted file mode 100644 index 838326468..000000000 --- a/validator/impl/collate-query-impl.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - 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 . - - Copyright 2017-2020 Telegram Systems LLP -*/ -#pragma once - -#include "validator/interfaces/validator-manager.h" - -namespace ton { - -namespace validator { - -class CollateQuery : public td::actor::Actor { - public: - CollateQuery(ShardIdFull shard, td::uint32 min_ts, BlockIdExt min_masterchain_block_id, std::vector prev, - td::Ref validator_set, td::actor::ActorId manager, td::Timestamp timeout, - td::Promise promise); - CollateQuery(ShardIdFull shard, td::uint32 min_ts, BlockIdExt min_masterchain_block_id, ZeroStateIdExt zero_state_id, - td::Ref validator_set, td::actor::ActorId manager, td::Timestamp timeout, - td::Promise promise); - - void alarm() override; - - void abort_query(td::Status reason); - void finish_query(); - - void start_up() override; - void got_prev_state(td::Ref state); - void written_block_data(); - void written_block_collated_data(); - - private: - ShardIdFull shard_; - UnixTime min_ts_; - BlockIdExt min_masterchain_block_id_; - std::vector prev_; - ZeroStateIdExt zero_state_id_; - td::Ref validator_set_; - td::actor::ActorId manager_; - td::Timestamp timeout_; - td::Promise promise_; - - BlockCandidate candidate_; - UnixTime ts_; -}; - -} // namespace validator - -} // namespace ton diff --git a/validator/impl/collator-impl.h b/validator/impl/collator-impl.h index b8d9e56d3..b1594a4cc 100644 --- a/validator/impl/collator-impl.h +++ b/validator/impl/collator-impl.h @@ -65,7 +65,6 @@ class Collator final : public td::actor::Actor { bool libraries_changed_{false}; bool prev_key_block_exists_{false}; bool is_hardfork_{false}; - UnixTime min_ts; BlockIdExt min_mc_block_id; std::vector prev_blocks; std::vector> prev_states; @@ -77,6 +76,9 @@ class Collator final : public td::actor::Actor { td::Timestamp timeout; td::Timestamp queue_cleanup_timeout_, soft_timeout_, medium_timeout_; td::Promise main_promise; + unsigned mode_ = 0; + int attempt_idx_; + bool allow_repeat_collation_ = false; ton::BlockSeqno last_block_seqno{0}; ton::BlockSeqno prev_mc_block_seqno{0}; ton::BlockSeqno new_block_seqno{0}; @@ -89,10 +91,10 @@ class Collator final : public td::actor::Actor { static constexpr bool shard_splitting_enabled = true; public: - Collator(ShardIdFull shard, bool is_hardfork, td::uint32 min_ts, BlockIdExt min_masterchain_block_id, - std::vector prev, Ref validator_set, Ed25519_PublicKey collator_id, - Ref collator_opts, td::actor::ActorId manager, td::Timestamp timeout, - td::Promise promise); + Collator(ShardIdFull shard, bool is_hardfork, BlockIdExt min_masterchain_block_id, std::vector prev, + Ref validator_set, Ed25519_PublicKey collator_id, Ref collator_opts, + td::actor::ActorId manager, td::Timestamp timeout, td::Promise promise, + td::CancellationToken cancellation_token, unsigned mode, int attempt_idx); ~Collator() override = default; bool is_busy() const { return busy_; @@ -218,6 +220,10 @@ class Collator final : public td::actor::Actor { td::uint64 defer_out_queue_size_limit_; td::uint64 hard_defer_out_queue_size_limit_; + std::unique_ptr account_dict_estimator_; + std::set account_dict_estimator_added_accounts_; + unsigned account_dict_ops_{0}; + bool msg_metadata_enabled_ = false; bool deferring_messages_enabled_ = false; bool store_out_msg_queue_size_ = false; @@ -320,6 +326,8 @@ class Collator final : public td::actor::Actor { bool insert_out_msg(Ref out_msg); bool insert_out_msg(Ref out_msg, td::ConstBitPtr msg_hash); bool register_out_msg_queue_op(bool force = false); + bool register_dispatch_queue_op(bool force = false); + bool update_account_dict_estimation(const block::transaction::Transaction& trans); bool update_min_mc_seqno(ton::BlockSeqno some_mc_seqno); bool combine_account_transactions(); bool update_public_libraries(); @@ -350,10 +358,14 @@ class Collator final : public td::actor::Actor { bool create_block(); Ref collate_shard_block_descr_set(); bool create_collated_data(); + bool create_block_candidate(); void return_block_candidate(td::Result saved); bool update_last_proc_int_msg(const std::pair& new_lt_hash); + td::CancellationToken cancellation_token_; + bool check_cancelled(); + public: static td::uint32 get_skip_externals_queue_size(); diff --git a/validator/impl/collator.cpp b/validator/impl/collator.cpp index 2cf77bfa3..6b5d76144 100644 --- a/validator/impl/collator.cpp +++ b/validator/impl/collator.cpp @@ -1,7 +1,7 @@ /* - This file is part of TON Blockchain Library. + This file is part of TON Blockchain Library. - TON Blockchain Library is free software: you can redistribute it and/or modify + 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. @@ -45,11 +45,13 @@ using td::Ref; using namespace std::literals::string_literals; // Don't increase MERGE_MAX_QUEUE_LIMIT too much: merging requires cleaning the whole queue in out_msg_queue_cleanup -static const td::uint32 FORCE_SPLIT_QUEUE_SIZE = 4096; -static const td::uint32 SPLIT_MAX_QUEUE_SIZE = 100000; -static const td::uint32 MERGE_MAX_QUEUE_SIZE = 2047; -static const td::uint32 SKIP_EXTERNALS_QUEUE_SIZE = 8000; -static const int HIGH_PRIORITY_EXTERNAL = 10; // don't skip high priority externals when queue is big +static constexpr td::uint32 FORCE_SPLIT_QUEUE_SIZE = 4096; +static constexpr td::uint32 SPLIT_MAX_QUEUE_SIZE = 100000; +static constexpr td::uint32 MERGE_MAX_QUEUE_SIZE = 2047; +static constexpr td::uint32 SKIP_EXTERNALS_QUEUE_SIZE = 8000; +static constexpr int HIGH_PRIORITY_EXTERNAL = 10; // don't skip high priority externals when queue is big + +static constexpr int MAX_ATTEMPTS = 5; #define DBG(__n) dbg(__n)&& #define DSTART int __dcnt = 0; @@ -66,7 +68,6 @@ static inline bool dbg(int c) { * * @param shard The shard of the new block. * @param is_hardfork A boolean indicating whether the new block is a hardfork. - * @param min_ts The minimum UnixTime for the new block. * @param min_masterchain_block_id The the minimum reference masterchain block. * @param prev A vector of BlockIdExt representing the previous blocks. * @param validator_set A reference to the ValidatorSet. @@ -75,14 +76,17 @@ static inline bool dbg(int c) { * @param manager The ActorId of the ValidatorManager. * @param timeout The timeout for the collator. * @param promise The promise to return the result. + * @param cancellation_token Token to cancel collation. + * @param mode +1 - skip storing candidate to disk. + * @param attempt_idx The index of the attempt, starting from 0. On later attempts collator decreases block limits and skips some steps. */ -Collator::Collator(ShardIdFull shard, bool is_hardfork, UnixTime min_ts, BlockIdExt min_masterchain_block_id, - std::vector prev, Ref validator_set, Ed25519_PublicKey collator_id, +Collator::Collator(ShardIdFull shard, bool is_hardfork, BlockIdExt min_masterchain_block_id, + std::vector prev, td::Ref validator_set, Ed25519_PublicKey collator_id, Ref collator_opts, td::actor::ActorId manager, - td::Timestamp timeout, td::Promise promise) + td::Timestamp timeout, td::Promise promise, td::CancellationToken cancellation_token, + unsigned mode, int attempt_idx) : shard_(shard) , is_hardfork_(is_hardfork) - , min_ts(min_ts) , min_mc_block_id{min_masterchain_block_id} , prev_blocks(std::move(prev)) , created_by_(collator_id) @@ -95,9 +99,13 @@ Collator::Collator(ShardIdFull shard, bool is_hardfork, UnixTime min_ts, BlockId , soft_timeout_(td::Timestamp::at(timeout.at() - 3.0)) , medium_timeout_(td::Timestamp::at(timeout.at() - 1.5)) , main_promise(std::move(promise)) - , perf_timer_("collate", 0.1, [manager](double duration) { - send_closure(manager, &ValidatorManager::add_perf_timer_stat, "collate", duration); - }) { + , mode_(mode) + , attempt_idx_(attempt_idx) + , perf_timer_("collate", 0.1, + [manager](double duration) { + send_closure(manager, &ValidatorManager::add_perf_timer_stat, "collate", duration); + }) + , cancellation_token_(std::move(cancellation_token)) { } /** @@ -109,7 +117,11 @@ Collator::Collator(ShardIdFull shard, bool is_hardfork, UnixTime min_ts, BlockId * The results of these queries are handled by corresponding callback functions. */ void Collator::start_up() { - LOG(WARNING) << "Collator for shard " << shard_.to_str() << " started"; + LOG(WARNING) << "Collator for shard " << shard_.to_str() << " started" + << (attempt_idx_ ? PSTRING() << " (attempt #" << attempt_idx_ << ")" : ""); + if (!check_cancelled()) { + return; + } LOG(DEBUG) << "Previous block #1 is " << prev_blocks.at(0).to_str(); if (prev_blocks.size() > 1) { LOG(DEBUG) << "Previous block #2 is " << prev_blocks.at(1).to_str(); @@ -342,7 +354,15 @@ bool Collator::fatal_error(td::Status error) { error.ensure_error(); LOG(ERROR) << "cannot generate block candidate for " << show_shard(shard_) << " : " << error.to_string(); if (busy_) { - main_promise(std::move(error)); + if (allow_repeat_collation_ && error.code() != ErrorCode::cancelled && attempt_idx_ + 1 < MAX_ATTEMPTS && + !is_hardfork_ && !timeout.is_in_past()) { + LOG(WARNING) << "Repeating collation (attempt #" << attempt_idx_ + 1 << ")"; + run_collate_query(shard_, min_mc_block_id, prev_blocks, created_by_, validator_set_, collator_opts_, manager, + td::Timestamp::in(10.0), std::move(main_promise), std::move(cancellation_token_), mode_, + attempt_idx_ + 1); + } else { + main_promise(std::move(error)); + } busy_ = false; } stop(); @@ -384,6 +404,9 @@ bool Collator::fatal_error(std::string err_msg, int err_code) { */ void Collator::check_pending() { // LOG(DEBUG) << "pending = " << pending; + if (!check_cancelled()) { + return; + } if (!pending) { step = 2; try { @@ -714,6 +737,15 @@ bool Collator::unpack_last_mc_state() { return fatal_error(limits.move_as_error()); } block_limits_ = limits.move_as_ok(); + if (attempt_idx_ == 3) { + LOG(INFO) << "Attempt #3: bytes, gas limits /= 2"; + block_limits_->bytes.multiply_by(0.5); + block_limits_->gas.multiply_by(0.5); + } else if (attempt_idx_ == 4) { + LOG(INFO) << "Attempt #4: bytes, gas limits /= 4"; + block_limits_->bytes.multiply_by(0.25); + block_limits_->gas.multiply_by(0.25); + } LOG(DEBUG) << "block limits: bytes [" << block_limits_->bytes.underload() << ", " << block_limits_->bytes.soft() << ", " << block_limits_->bytes.hard() << "]"; LOG(DEBUG) << "block limits: gas [" << block_limits_->gas.underload() << ", " << block_limits_->gas.soft() << ", " @@ -1032,7 +1064,7 @@ bool Collator::split_last_state(block::ShardState& ss) { /** * Imports the shard state data into the Collator object. * - * SETS: account_dict, shard_libraries_, mc_state_extra + * SETS: account_dict = account_dict_estimator_, shard_libraries_, mc_state_extra * total_balance_ = old_total_balance_, total_validator_fees_ * SETS: overload_history_, underload_history_ * SETS: prev_state_utime_, prev_state_lt_, prev_vert_seqno_ @@ -1044,6 +1076,7 @@ bool Collator::split_last_state(block::ShardState& ss) { */ bool Collator::import_shard_state_data(block::ShardState& ss) { account_dict = std::move(ss.account_dict_); + account_dict_estimator_ = std::make_unique(*account_dict); shard_libraries_ = std::move(ss.shard_libraries_); mc_state_extra_ = std::move(ss.mc_state_extra_); overload_history_ = ss.overload_history_; @@ -2095,6 +2128,7 @@ bool Collator::do_collate() { if (max_lt == start_lt) { ++max_lt; } + allow_repeat_collation_ = true; // NB: interchanged 1.2 and 1.1 (is this always correct?) // 1.1. re-adjust neighbors' out_msg_queues (for oneself) if (!add_trivial_neighbor()) { @@ -2335,6 +2369,9 @@ bool Collator::out_msg_queue_cleanup() { LOG(WARNING) << "cleaning up outbound queue takes too long, ending"; break; } + if (!check_cancelled()) { + return false; + } if (i == queue_parts.size()) { i = 0; } @@ -2706,6 +2743,9 @@ bool Collator::create_ticktock_transaction(const ton::StdSmcAddress& smc_addr, t return fatal_error( td::Status::Error(-666, std::string{"cannot commit new transaction for smart contract "} + smc_addr.to_hex())); } + if (!update_account_dict_estimation(*trans)) { + return fatal_error(-666, "cannot update account dict size estimation"); + } update_max_lt(acc->last_trans_end_lt_); block::MsgMetadata new_msg_metadata{0, acc->workchain, acc->addr, trans->start_lt}; register_new_msgs(*trans, std::move(new_msg_metadata)); @@ -2799,6 +2839,10 @@ Ref Collator::create_ordinary_transaction(Ref msg_root, fatal_error("cannot commit new transaction for smart contract "s + addr.to_hex()); return {}; } + if (!update_account_dict_estimation(*trans)) { + fatal_error("cannot update account dict size estimation"); + return {}; + } td::optional new_msg_metadata; if (external || is_special_tx) { @@ -3534,6 +3578,9 @@ bool Collator::process_inbound_internal_messages() { stats_.limits_log += PSTRING() << "INBOUND_INT_MESSAGES: timeout\n"; break; } + if (!check_cancelled()) { + return false; + } auto kv = nb_out_msgs_->extract_cur(); CHECK(kv && kv->msg.not_null()); LOG(DEBUG) << "processing inbound message with (lt,hash)=(" << kv->lt << "," << kv->key.to_hex() @@ -3567,6 +3614,10 @@ bool Collator::process_inbound_external_messages() { LOG(INFO) << "skipping processing of inbound external messages"; return true; } + if (attempt_idx_ >= 2) { + LOG(INFO) << "Attempt #" << attempt_idx_ << ": skip external messages"; + return true; + } if (out_msg_queue_size_ > SKIP_EXTERNALS_QUEUE_SIZE) { LOG(INFO) << "skipping processing of inbound external messages (except for high-priority) because out_msg_queue is " "too big (" @@ -3588,6 +3639,9 @@ bool Collator::process_inbound_external_messages() { stats_.limits_log += PSTRING() << "INBOUND_EXT_MESSAGES: timeout\n"; break; } + if (!check_cancelled()) { + return false; + } auto ext_msg = ext_msg_struct.cell; ton::Bits256 hash{ext_msg->get_hash().bits()}; int r = process_external_message(std::move(ext_msg)); @@ -3694,6 +3748,10 @@ bool Collator::process_dispatch_queue() { if (max_per_initiator[iter] == 0 || max_total_count[iter] == 0) { continue; } + if (iter > 0 && attempt_idx_ >= 1) { + LOG(INFO) << "Attempt #" << attempt_idx_ << ": skip process_dispatch_queue"; + break; + } vm::AugmentedDictionary cur_dispatch_queue{dispatch_queue_->get_root(), 256, block::tlb::aug_DispatchQueue}; std::map, size_t> count_per_initiator; size_t total_count = 0; @@ -3706,13 +3764,13 @@ bool Collator::process_dispatch_queue() { stats_.limits_log += PSTRING() << "DISPATCH_QUEUE_STAGE_" << iter << ": " << block_full_comment(*block_limit_status_, block::ParamLimits::cl_normal) << "\n"; - return true; + return register_dispatch_queue_op(true); } if (soft_timeout_.is_in_past(td::Timestamp::now())) { block_full_ = true; LOG(WARNING) << "soft timeout reached, stop processing dispatch queue"; stats_.limits_log += PSTRING() << "DISPATCH_QUEUE_STAGE_" << iter << ": timeout\n"; - return true; + return register_dispatch_queue_op(true); } StdSmcAddress src_addr; td::Ref account_dispatch_queue; @@ -3790,6 +3848,7 @@ bool Collator::process_dispatch_queue() { if (iter == 0) { have_unprocessed_account_dispatch_queue_ = false; } + register_dispatch_queue_op(true); } return true; } @@ -3813,12 +3872,7 @@ bool Collator::process_deferred_message(Ref enq_msg, StdSmcAddres return fatal_error(PSTRING() << "failed to delete message from DispatchQueue: address=" << src_addr.to_hex() << ", lt=" << lt); } - ++dispatch_queue_ops_; - if (!(dispatch_queue_ops_ & 63)) { - if (!block_limit_status_->add_proof(dispatch_queue_->get_root_cell())) { - return false; - } - } + register_dispatch_queue_op(); ++sender_generated_messages_count_[src_addr]; LogicalTime enqueued_lt = 0; @@ -3911,6 +3965,7 @@ bool Collator::process_deferred_message(Ref enq_msg, StdSmcAddres ++unprocessed_deferred_messages_[src_addr]; LOG(INFO) << "delivering deferred message from account " << src_addr.to_hex() << ", lt=" << lt << ", emitted_lt=" << emitted_lt; + block_limit_status_->add_cell(msg_env); register_new_msg(std::move(new_msg)); msg_metadata = std::move(env.metadata); return true; @@ -4090,11 +4145,7 @@ bool Collator::enqueue_message(block::NewOutMsg msg, td::RefInt256 fwd_fees_rema } ++dispatch_dict_size; dispatch_queue_->set(src_addr, block::pack_account_dispatch_queue(dispatch_dict, dispatch_dict_size)); - ++dispatch_queue_ops_; - if (!(dispatch_queue_ops_ & 63)) { - return block_limit_status_->add_proof(dispatch_queue_->get_root_cell()); - } - return true; + return register_dispatch_queue_op(); } auto next_hop = block::interpolate_addr(src_prefix, dest_prefix, route_info.second); @@ -4136,6 +4187,9 @@ bool Collator::process_new_messages(bool enqueue_only) { stats_.limits_log += PSTRING() << "NEW_MESSAGES: " << block_full_comment(*block_limit_status_, block::ParamLimits::cl_normal) << "\n"; } + if (!check_cancelled()) { + return false; + } LOG(DEBUG) << "have message with lt=" << msg.lt; int res = process_one_new_message(std::move(msg), enqueue_only); if (res < 0) { @@ -4462,7 +4516,7 @@ bool Collator::create_mc_state_extra() { cc_updated = true; LOG(INFO) << "increased masterchain catchain seqno to " << val_info.catchain_seqno; } - auto nodes = block::Config::do_compute_validator_set(ccvc, shard_, *cur_validators, now_, val_info.catchain_seqno); + auto nodes = block::Config::do_compute_validator_set(ccvc, shard_, *cur_validators, val_info.catchain_seqno); LOG_CHECK(!nodes.empty()) << "validator node list in unpacked validator set is empty"; auto vlist_hash = block::compute_validator_set_hash(/* val_info.catchain_seqno */ 0, shard_, std::move(nodes)); @@ -4777,11 +4831,11 @@ bool Collator::check_block_overload() { } char buffer[17]; if (history_weight(overload_history_) >= 0) { - sprintf(buffer, "%016llx", (unsigned long long)overload_history_); + snprintf(buffer, sizeof(buffer), "%016llx", (unsigned long long)overload_history_); LOG(INFO) << "want_split set because of overload history " << buffer; want_split_ = true; } else if (history_weight(underload_history_) >= 0) { - sprintf(buffer, "%016llx", (unsigned long long)underload_history_); + snprintf(buffer, sizeof(buffer), "%016llx", (unsigned long long)underload_history_); LOG(INFO) << "want_merge set because of underload history " << buffer; want_merge_ = true; } @@ -4975,6 +5029,56 @@ bool Collator::register_out_msg_queue_op(bool force) { } } +/** + * Registers a dispatch queue message queue operation. + * Adds the proof to the block limit status every 64 operations. + * + * @param force If true, the proof will always be added to the block limit status. + * + * @returns True if the operation was successfully registered, false otherwise. + */ +bool Collator::register_dispatch_queue_op(bool force) { + ++dispatch_queue_ops_; + if (force || !(dispatch_queue_ops_ & 63)) { + return block_limit_status_->add_proof(dispatch_queue_->get_root_cell()); + } else { + return true; + } +} + +/** + * Update size estimation for the account dictionary. + * This is required to count the depth of the ShardAccounts dictionary in the block size estimation. + * account_dict_estimator_ is used for block limits only. + * + * @param trans Newly-created transaction. + * + * @returns True on success, false otherwise. + */ +bool Collator::update_account_dict_estimation(const block::transaction::Transaction& trans) { + const block::Account& acc = trans.account; + if (acc.orig_total_state->get_hash() != acc.total_state->get_hash() && + account_dict_estimator_added_accounts_.insert(acc.addr).second) { + // see combine_account_transactions + if (acc.status == block::Account::acc_nonexist) { + account_dict_estimator_->lookup_delete(acc.addr); + } else { + vm::CellBuilder cb; + if (!(cb.store_ref_bool(acc.total_state) // account_descr$_ account:^Account + && cb.store_bits_bool(acc.last_trans_hash_) // last_trans_hash:bits256 + && cb.store_long_bool(acc.last_trans_lt_, 64) // last_trans_lt:uint64 + && account_dict_estimator_->set_builder(acc.addr, cb))) { + return false; + } + } + } + ++account_dict_ops_; + if (!(account_dict_ops_ & 15)) { + return block_limit_status_->add_proof(account_dict_estimator_->get_root_cell()); + } + return true; +} + /** * Creates a new shard state and the Merkle update. * @@ -5100,9 +5204,10 @@ bool Collator::compute_out_msg_queue_info(Ref& out_msg_queue_info) { vm::CellSlice maybe_extra = cb.as_cellslice(); cb.reset(); - return register_out_msg_queue_op(true) && out_msg_queue_->append_dict_to_bool(cb) // _ out_queue:OutMsgQueue - && processed_upto_->pack(cb) // proc_info:ProcessedInfo - && cb.append_cellslice_bool(maybe_extra) // extra:(Maybe OutMsgQueueExtra) + return register_out_msg_queue_op(true) && register_dispatch_queue_op(true) && + out_msg_queue_->append_dict_to_bool(cb) // _ out_queue:OutMsgQueue + && processed_upto_->pack(cb) // proc_info:ProcessedInfo + && cb.append_cellslice_bool(maybe_extra) // extra:(Maybe OutMsgQueueExtra) && cb.finalize_to(out_msg_queue_info); } @@ -5494,14 +5599,18 @@ bool Collator::create_block_candidate() { << consensus_config.max_collated_data_size << ")"); } // 4. save block candidate - LOG(INFO) << "saving new BlockCandidate"; - td::actor::send_closure_later( - manager, &ValidatorManager::set_block_candidate, block_candidate->id, block_candidate->clone(), - validator_set_->get_catchain_seqno(), validator_set_->get_validator_set_hash(), - [self = get_self()](td::Result saved) -> void { - LOG(DEBUG) << "got answer to set_block_candidate"; - td::actor::send_closure_later(std::move(self), &Collator::return_block_candidate, std::move(saved)); - }); + if (mode_ & CollateMode::skip_store_candidate) { + td::actor::send_closure_later(actor_id(this), &Collator::return_block_candidate, td::Unit()); + } else { + LOG(INFO) << "saving new BlockCandidate"; + td::actor::send_closure_later( + manager, &ValidatorManager::set_block_candidate, block_candidate->id, block_candidate->clone(), + validator_set_->get_catchain_seqno(), validator_set_->get_validator_set_hash(), + [self = get_self()](td::Result saved) -> void { + LOG(DEBUG) << "got answer to set_block_candidate"; + td::actor::send_closure_later(std::move(self), &Collator::return_block_candidate, std::move(saved)); + }); + } // 5. communicate about bad and delayed external messages if (!bad_ext_msgs_.empty() || !delay_ext_msgs_.empty()) { LOG(INFO) << "sending complete_external_messages() to Manager"; @@ -5645,6 +5754,18 @@ void Collator::after_get_external_messages(td::Result create_collator( - td::actor::ActorId block_db, - ShardIdFull shard /* , td::actor::ActorId validator_manager */); - virtual void generate_block_candidate(ShardIdFull shard, td::Promise promise) = 0; - virtual td::Result register_external_message_cell(Ref ext_msg) = 0; - virtual td::Result register_external_message(td::Slice ext_msg_boc) = 0; - virtual td::Result register_ihr_message_cell(Ref ihr_msg) = 0; - virtual td::Result register_ihr_message(td::Slice ihr_msg_boc) = 0; - virtual td::Result register_shard_signatures_cell(Ref shard_blk_signatures) = 0; - virtual td::Result register_shard_signatures(td::Slice shard_blk_signatures_boc) = 0; -}; - } // namespace ton diff --git a/validator/impl/fabric.cpp b/validator/impl/fabric.cpp index d69492393..65b922624 100644 --- a/validator/impl/fabric.cpp +++ b/validator/impl/fabric.cpp @@ -131,11 +131,11 @@ td::Result> create_ihr_message(td::BufferSlice data) { void run_accept_block_query(BlockIdExt id, td::Ref data, std::vector prev, td::Ref validator_set, td::Ref signatures, - td::Ref approve_signatures, bool send_broadcast, + td::Ref approve_signatures, int send_broadcast_mode, td::actor::ActorId manager, td::Promise promise) { - td::actor::create_actor("accept", id, std::move(data), prev, std::move(validator_set), - std::move(signatures), std::move(approve_signatures), send_broadcast, - manager, std::move(promise)) + td::actor::create_actor( + PSTRING() << "accept" << id.id.to_str(), id, std::move(data), prev, std::move(validator_set), + std::move(signatures), std::move(approve_signatures), send_broadcast_mode, manager, std::move(promise)) .release(); } @@ -192,7 +192,7 @@ void run_check_proof_link_query(BlockIdExt id, td::Ref proof, td::act .release(); } -void run_validate_query(ShardIdFull shard, UnixTime min_ts, BlockIdExt min_masterchain_block_id, +void run_validate_query(ShardIdFull shard, BlockIdExt min_masterchain_block_id, std::vector prev, BlockCandidate candidate, td::Ref validator_set, td::actor::ActorId manager, td::Timestamp timeout, td::Promise promise, bool is_fake) { @@ -205,26 +205,28 @@ void run_validate_query(ShardIdFull shard, UnixTime min_ts, BlockIdExt min_maste static std::atomic idx; td::actor::create_actor(PSTRING() << (is_fake ? "fakevalidate" : "validateblock") << shard.to_str() << ":" << (seqno + 1) << "#" << idx.fetch_add(1), - shard, min_ts, min_masterchain_block_id, std::move(prev), std::move(candidate), + shard, min_masterchain_block_id, std::move(prev), std::move(candidate), std::move(validator_set), std::move(manager), timeout, std::move(promise), is_fake) .release(); } -void run_collate_query(ShardIdFull shard, td::uint32 min_ts, const BlockIdExt& min_masterchain_block_id, - std::vector prev, Ed25519_PublicKey collator_id, td::Ref validator_set, +void run_collate_query(ShardIdFull shard, const BlockIdExt& min_masterchain_block_id, std::vector prev, + Ed25519_PublicKey creator, td::Ref validator_set, td::Ref collator_opts, td::actor::ActorId manager, - td::Timestamp timeout, td::Promise promise) { + td::Timestamp timeout, td::Promise promise, + td::CancellationToken cancellation_token, unsigned mode, int attempt_idx) { BlockSeqno seqno = 0; for (auto& p : prev) { if (p.seqno() > seqno) { seqno = p.seqno(); } } - td::actor::create_actor(PSTRING() << "collate" << shard.to_str() << ":" << (seqno + 1), shard, false, - min_ts, min_masterchain_block_id, std::move(prev), std::move(validator_set), - collator_id, std::move(collator_opts), std::move(manager), timeout, - std::move(promise)) + td::actor::create_actor(PSTRING() << "collate" << shard.to_str() << ":" << (seqno + 1) + << (attempt_idx ? "_" + td::to_string(attempt_idx) : ""), + shard, false, min_masterchain_block_id, std::move(prev), std::move(validator_set), + creator, std::move(collator_opts), std::move(manager), timeout, std::move(promise), + std::move(cancellation_token), mode, attempt_idx) .release(); } @@ -237,10 +239,10 @@ void run_collate_hardfork(ShardIdFull shard, const BlockIdExt& min_masterchain_b seqno = p.seqno(); } } - td::actor::create_actor(PSTRING() << "collate" << shard.to_str() << ":" << (seqno + 1), shard, true, 0, + td::actor::create_actor(PSTRING() << "collate" << shard.to_str() << ":" << (seqno + 1), shard, true, min_masterchain_block_id, std::move(prev), td::Ref{}, Ed25519_PublicKey{Bits256::zero()}, td::Ref{true}, - std::move(manager), timeout, std::move(promise)) + std::move(manager), timeout, std::move(promise), td::CancellationToken{}, 0, 0) .release(); } diff --git a/validator/impl/liteserver.cpp b/validator/impl/liteserver.cpp index 7bedf7fe4..6bd4e4219 100644 --- a/validator/impl/liteserver.cpp +++ b/validator/impl/liteserver.cpp @@ -290,6 +290,13 @@ void LiteQuery::perform() { [&](lite_api::liteServer_getBlockOutMsgQueueSize& q) { this->perform_getBlockOutMsgQueueSize(q.mode_, create_block_id(q.id_)); }, + [&](lite_api::liteServer_getDispatchQueueInfo& q) { + this->perform_getDispatchQueueInfo(q.mode_, create_block_id(q.id_), q.after_addr_, q.max_accounts_); + }, + [&](lite_api::liteServer_getDispatchQueueMessages& q) { + this->perform_getDispatchQueueMessages(q.mode_, create_block_id(q.id_), q.addr_, + std::max(q.after_lt_, 0), q.max_messages_); + }, [&](auto& obj) { this->abort_query(td::Status::Error(ErrorCode::protoviolation, "unknown query")); })); } @@ -1370,13 +1377,9 @@ void LiteQuery::finish_getAccountState(td::BufferSlice shard_proof) { if (acc_root.not_null()) { if (mode_ & 0x40000000) { vm::MerkleProofBuilder mpb{acc_root}; - // account_none$0 = Account; - // account$1 addr:MsgAddressInt storage_stat:StorageInfo storage:AccountStorage = Account; - // account_storage$_ last_trans_lt:uint64 balance:CurrencyCollection state:AccountState = AccountStorage; - // account_active$1 _:StateInit = AccountState; - auto S = mpb.root()->load_cell(); - if (S.is_error()) { - fatal_error(S.move_as_error_prefix("Failed to load account: ")); + // This does not include code, data and libs into proof, but it includes extra currencies + if (!block::gen::t_Account.validate_ref(mpb.root())) { + fatal_error("failed to validate Account"); return; } if (!mpb.extract_proof_to(acc_root)) { @@ -1853,7 +1856,7 @@ void LiteQuery::perform_getConfigParams(BlockIdExt blkid, int mode, std::vector< request_mc_block_data_state(blkid); } else { // get configuration from previous key block - load_prevKeyBlock(blkid, [this, blkid, mode, param_list = std::move(param_list)]( + load_prevKeyBlock(blkid, [this, mode, param_list = std::move(param_list)]( td::Result>> res) mutable { if (res.is_error()) { this->abort_query(res.move_as_error()); @@ -2050,7 +2053,7 @@ void LiteQuery::perform_lookupBlockWithProof(BlockId blkid, BlockIdExt mc_blkid, ton::AccountIdPrefixFull pfx{blkid.workchain, blkid.shard}; auto P = td::PromiseCreator::lambda( - [Self = actor_id(this), mc_blkid, manager = manager_, mode, pfx](td::Result res) { + [Self = actor_id(this), mc_blkid, manager = manager_, pfx](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); return; @@ -2066,7 +2069,7 @@ void LiteQuery::perform_lookupBlockWithProof(BlockId blkid, BlockIdExt mc_blkid, } LOG(DEBUG) << "requesting data for block " << handle->id().to_str(); td::actor::send_closure_later(manager, &ValidatorManager::get_block_data_from_db, handle, - [Self, mc_ref_blkid = handle->masterchain_ref_block(), mc_blkid, pfx, mode](td::Result> res) { + [Self, mc_ref_blkid = handle->masterchain_ref_block(), pfx](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -3432,6 +3435,248 @@ void LiteQuery::finish_getBlockOutMsgQueueSize() { finish_query(std::move(b)); } +void LiteQuery::perform_getDispatchQueueInfo(int mode, BlockIdExt blkid, StdSmcAddress after_addr, int max_accounts) { + LOG(INFO) << "started a getDispatchQueueInfo(" << blkid.to_str() << ", " << mode << ") liteserver query"; + mode_ = mode; + if (!blkid.is_valid_full()) { + fatal_error("invalid BlockIdExt"); + return; + } + if (max_accounts <= 0) { + fatal_error("invalid max_accounts"); + return; + } + set_continuation([=]() -> void { finish_getDispatchQueueInfo(after_addr, max_accounts); }); + request_block_data_state(blkid); +} + +void LiteQuery::finish_getDispatchQueueInfo(StdSmcAddress after_addr, int max_accounts) { + LOG(INFO) << "completing getDispatchQueueInfo() query"; + bool with_proof = mode_ & 1; + Ref state_root = state_->root_cell(); + vm::MerkleProofBuilder pb; + if (with_proof) { + pb = vm::MerkleProofBuilder{state_root}; + state_root = pb.root(); + } + + std::unique_ptr dispatch_queue; + + block::gen::ShardStateUnsplit::Record sstate; + block::gen::OutMsgQueueInfo::Record out_msg_queue_info; + block::gen::OutMsgQueueExtra::Record out_msg_queue_extra; + if (!tlb::unpack_cell(state_root, sstate) || !tlb::unpack_cell(sstate.out_msg_queue_info, out_msg_queue_info)) { + fatal_error("cannot unpack shard state"); + return; + } + vm::CellSlice& extra_slice = out_msg_queue_info.extra.write(); + if (extra_slice.fetch_long(1)) { + if (!tlb::unpack(extra_slice, out_msg_queue_extra)) { + fatal_error("cannot unpack OutMsgQueueExtra"); + return; + } + dispatch_queue = std::make_unique(out_msg_queue_extra.dispatch_queue, 256, + block::tlb::aug_DispatchQueue); + } else { + dispatch_queue = std::make_unique(256, block::tlb::aug_DispatchQueue); + } + + int remaining = std::min(max_accounts, 64); + bool complete = false; + std::vector> result; + bool allow_eq; + if (mode_ & 2) { + allow_eq = false; + } else { + allow_eq = true; + after_addr = td::Bits256::zero(); + } + while (true) { + auto value = dispatch_queue->extract_value(dispatch_queue->lookup_nearest_key(after_addr, true, allow_eq)); + allow_eq = false; + if (value.is_null()) { + complete = true; + break; + } + if (remaining == 0) { + break; + } + --remaining; + StdSmcAddress addr = after_addr; + vm::Dictionary dict{64}; + td::uint64 dict_size; + if (!block::unpack_account_dispatch_queue(value, dict, dict_size)) { + fatal_error(PSTRING() << "invalid account dispatch queue for account " << addr.to_hex()); + return; + } + CHECK(dict_size > 0); + td::BitArray<64> min_lt, max_lt; + dict.get_minmax_key(min_lt.bits(), 64, false, false); + dict.get_minmax_key(max_lt.bits(), 64, true, false); + result.push_back(create_tl_object(addr, dict_size, min_lt.to_ulong(), + max_lt.to_ulong())); + } + + td::BufferSlice proof; + if (with_proof) { + Ref proof1, proof2; + if (!make_state_root_proof(proof1)) { + return; + } + if (!pb.extract_proof_to(proof2)) { + fatal_error("unknown error creating Merkle proof"); + return; + } + auto r_proof = vm::std_boc_serialize_multi({std::move(proof1), std::move(proof2)}); + if (r_proof.is_error()) { + fatal_error(r_proof.move_as_error()); + return; + } + proof = r_proof.move_as_ok(); + } + LOG(INFO) << "getDispatchQueueInfo(" << blk_id_.to_str() << ", " << mode_ << ") query completed"; + auto b = ton::create_serialize_tl_object( + mode_, ton::create_tl_lite_block_id(blk_id_), std::move(result), complete, std::move(proof)); + finish_query(std::move(b)); +} + +void LiteQuery::perform_getDispatchQueueMessages(int mode, BlockIdExt blkid, StdSmcAddress addr, LogicalTime lt, + int max_messages) { + LOG(INFO) << "started a getDispatchQueueMessages(" << blkid.to_str() << ", " << mode << ") liteserver query"; + mode_ = mode; + if (!blkid.is_valid_full()) { + fatal_error("invalid BlockIdExt"); + return; + } + if (max_messages <= 0) { + fatal_error("invalid max_messages"); + return; + } + set_continuation([=]() -> void { finish_getDispatchQueueMessages(addr, lt, max_messages); }); + request_block_data_state(blkid); +} + +void LiteQuery::finish_getDispatchQueueMessages(StdSmcAddress addr, LogicalTime lt, int max_messages) { + LOG(INFO) << "completing getDispatchQueueMessages() query"; + bool with_proof = mode_ & lite_api::liteServer_getDispatchQueueMessages::WANT_PROOF_MASK; + bool one_account = mode_ & lite_api::liteServer_getDispatchQueueMessages::ONE_ACCOUNT_MASK; + bool with_messages_boc = mode_ & lite_api::liteServer_getDispatchQueueMessages::MESSAGES_BOC_MASK; + Ref state_root = state_->root_cell(); + vm::MerkleProofBuilder pb; + if (with_proof) { + pb = vm::MerkleProofBuilder{state_root}; + state_root = pb.root(); + } + + std::unique_ptr dispatch_queue; + + block::gen::ShardStateUnsplit::Record sstate; + block::gen::OutMsgQueueInfo::Record out_msg_queue_info; + block::gen::OutMsgQueueExtra::Record out_msg_queue_extra; + if (!tlb::unpack_cell(state_root, sstate) || !tlb::unpack_cell(sstate.out_msg_queue_info, out_msg_queue_info)) { + fatal_error("cannot unpack shard state"); + return; + } + vm::CellSlice& extra_slice = out_msg_queue_info.extra.write(); + if (extra_slice.fetch_long(1)) { + if (!tlb::unpack(extra_slice, out_msg_queue_extra)) { + fatal_error("cannot unpack OutMsgQueueExtra"); + return; + } + dispatch_queue = std::make_unique(out_msg_queue_extra.dispatch_queue, 256, + block::tlb::aug_DispatchQueue); + } else { + dispatch_queue = std::make_unique(256, block::tlb::aug_DispatchQueue); + } + + int remaining = std::min(max_messages, with_messages_boc ? 16 : 64); + bool complete = false; + std::vector> result; + std::vector> message_roots; + td::Bits256 orig_addr = addr; + bool first = true; + while (remaining > 0) { + auto value = dispatch_queue->extract_value(dispatch_queue->lookup_nearest_key(addr, true, first)); + if (value.is_null() || (one_account && addr != orig_addr)) { + complete = true; + break; + } + vm::Dictionary account_queue{64}; + td::uint64 dict_size; + if (!block::unpack_account_dispatch_queue(value, account_queue, dict_size)) { + fatal_error(PSTRING() << "invalid account dispatch queue for account " << addr.to_hex()); + return; + } + CHECK(dict_size > 0); + while (true) { + td::BitArray<64> lt_key; + lt_key.store_ulong(lt); + auto value2 = account_queue.lookup_nearest_key(lt_key, true, false); + if (value2.is_null()) { + break; + } + lt = lt_key.to_ulong(); + if (remaining == 0) { + break; + } + --remaining; + auto msg_env = value2->prefetch_ref(); + block::tlb::MsgEnvelope::Record_std env; + if (msg_env.is_null() || !tlb::unpack_cell(msg_env, env)) { + fatal_error(PSTRING() << "invalid message in dispatch queue for account " << addr.to_hex() << ", lt " << lt); + return; + } + message_roots.push_back(env.msg); + tl_object_ptr metadata_tl; + if (env.metadata) { + auto& metadata = env.metadata.value(); + metadata_tl = create_tl_object( + 0, metadata.depth, + create_tl_object(metadata.initiator_wc, metadata.initiator_addr), + metadata.initiator_lt); + } else { + metadata_tl = create_tl_object( + 0, -1, create_tl_object(workchainInvalid, td::Bits256::zero()), -1); + } + result.push_back(create_tl_object(addr, lt, env.msg->get_hash().bits(), + std::move(metadata_tl))); + } + first = false; + lt = 0; + } + + td::BufferSlice proof; + if (with_proof) { + Ref proof1, proof2; + if (!make_state_root_proof(proof1)) { + return; + } + if (!pb.extract_proof_to(proof2)) { + fatal_error("unknown error creating Merkle proof"); + return; + } + auto r_proof = vm::std_boc_serialize_multi({std::move(proof1), std::move(proof2)}); + if (r_proof.is_error()) { + fatal_error(r_proof.move_as_error()); + return; + } + proof = r_proof.move_as_ok(); + } + td::BufferSlice messages_boc; + if (with_messages_boc) { + auto r_messages_boc = vm::std_boc_serialize_multi(std::move(message_roots)); + if (r_messages_boc.is_error()) { + fatal_error(r_messages_boc.move_as_error()); + return; + } + messages_boc = std::move(messages_boc); + } + LOG(INFO) << "getDispatchQueueMessages(" << blk_id_.to_str() << ", " << mode_ << ") query completed"; + auto b = ton::create_serialize_tl_object( + mode_, ton::create_tl_lite_block_id(blk_id_), std::move(result), complete, std::move(proof), + std::move(messages_boc)); + finish_query(std::move(b)); +} void LiteQuery::perform_nonfinal_getCandidate(td::Bits256 source, BlockIdExt blkid, td::Bits256 collated_data_hash) { LOG(INFO) << "started a nonfinal.getCandidate liteserver query"; diff --git a/validator/impl/liteserver.hpp b/validator/impl/liteserver.hpp index 2d75dc61c..447e1dad4 100644 --- a/validator/impl/liteserver.hpp +++ b/validator/impl/liteserver.hpp @@ -172,6 +172,11 @@ class LiteQuery : public td::actor::Actor { void continue_getOutMsgQueueSizes(td::optional shard, Ref state); void perform_getBlockOutMsgQueueSize(int mode, BlockIdExt blkid); void finish_getBlockOutMsgQueueSize(); + void perform_getDispatchQueueInfo(int mode, BlockIdExt blkid, StdSmcAddress after_addr, int max_accounts); + void finish_getDispatchQueueInfo(StdSmcAddress after_addr, int max_accounts); + void perform_getDispatchQueueMessages(int mode, BlockIdExt blkid, StdSmcAddress addr, LogicalTime lt, + int max_messages); + void finish_getDispatchQueueMessages(StdSmcAddress addr, LogicalTime lt, int max_messages); void perform_nonfinal_getCandidate(td::Bits256 source, BlockIdExt blkid, td::Bits256 collated_data_hash); void perform_nonfinal_getValidatorGroups(int mode, ShardIdFull shard); diff --git a/validator/impl/shard.cpp b/validator/impl/shard.cpp index 9c4245b6a..0ab216f71 100644 --- a/validator/impl/shard.cpp +++ b/validator/impl/shard.cpp @@ -290,6 +290,7 @@ td::Result, td::Ref>> ShardStateQ::spl } td::Result ShardStateQ::serialize() const { + TD_PERF_COUNTER(serialize_state); td::PerfWarningTimer perf_timer_{"serializestate", 0.1}; if (!data.is_null()) { return data.clone(); @@ -314,6 +315,7 @@ td::Result ShardStateQ::serialize() const { } td::Status ShardStateQ::serialize_to_file(td::FileFd& fd) const { + TD_PERF_COUNTER(serialize_state_to_file); td::PerfWarningTimer perf_timer_{"serializestate", 0.1}; if (!data.is_null()) { auto cur_data = data.clone(); @@ -375,7 +377,8 @@ td::Status MasterchainStateQ::mc_init() { td::Status MasterchainStateQ::mc_reinit() { auto res = block::ConfigInfo::extract_config( root_cell(), block::ConfigInfo::needStateRoot | block::ConfigInfo::needValidatorSet | - block::ConfigInfo::needShardHashes | block::ConfigInfo::needPrevBlocks); + block::ConfigInfo::needShardHashes | block::ConfigInfo::needPrevBlocks | + block::ConfigInfo::needWorkchainInfo); cur_validators_.reset(); next_validators_.reset(); if (res.is_error()) { @@ -521,15 +524,15 @@ bool MasterchainStateQ::check_old_mc_block_id(const ton::BlockIdExt& blkid, bool return config_ && config_->check_old_mc_block_id(blkid, strict); } -td::uint32 MasterchainStateQ::min_split_depth(WorkchainId workchain_id) const { +td::uint32 MasterchainStateQ::monitor_min_split_depth(WorkchainId workchain_id) const { if (!config_) { return 0; } auto wc_info = config_->get_workchain_info(workchain_id); - return wc_info.not_null() ? wc_info->actual_min_split : 0; + return wc_info.not_null() ? wc_info->monitor_min_split : 0; } -td::uint32 MasterchainStateQ::soft_min_split_depth(WorkchainId workchain_id) const { +td::uint32 MasterchainStateQ::min_split_depth(WorkchainId workchain_id) const { if (!config_) { return 0; } @@ -566,5 +569,9 @@ BlockIdExt MasterchainStateQ::prev_key_block_id(BlockSeqno seqno) const { return block_id; } +bool MasterchainStateQ::is_key_state() const { + return config_ ? config_->is_key_state() : false; +} + } // namespace validator } // namespace ton diff --git a/validator/impl/shard.hpp b/validator/impl/shard.hpp index d9a7dd655..d07d533e1 100644 --- a/validator/impl/shard.hpp +++ b/validator/impl/shard.hpp @@ -124,8 +124,8 @@ class MasterchainStateQ : public MasterchainState, public ShardStateQ { bool has_workchain(WorkchainId workchain) const { return config_ && config_->has_workchain(workchain); } + td::uint32 monitor_min_split_depth(WorkchainId workchain_id) const override; td::uint32 min_split_depth(WorkchainId workchain_id) const override; - td::uint32 soft_min_split_depth(WorkchainId workchain_id) const override; BlockSeqno min_ref_masterchain_seqno() const override; td::Status prepare() override; ZeroStateIdExt get_zerostate_id() const { @@ -141,6 +141,7 @@ class MasterchainStateQ : public MasterchainState, public ShardStateQ { BlockIdExt last_key_block_id() const override; BlockIdExt next_key_block_id(BlockSeqno seqno) const override; BlockIdExt prev_key_block_id(BlockSeqno seqno) const override; + bool is_key_state() const override; MasterchainStateQ* make_copy() const override; static td::Result> fetch(const BlockIdExt& _id, td::BufferSlice _data, diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index 003b7f9f7..8490567e8 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -57,7 +57,6 @@ std::string ErrorCtx::as_string() const { * Constructs a ValidateQuery object. * * @param shard The shard of the block being validated. - * @param min_ts The minimum allowed UnixTime for the block. * @param min_masterchain_block_id The minimum allowed masterchain block reference for the block. * @param prev A vector of BlockIdExt representing the previous blocks. * @param candidate The BlockCandidate to be validated. @@ -67,13 +66,12 @@ std::string ErrorCtx::as_string() const { * @param promise The Promise to return the ValidateCandidateResult to. * @param is_fake A boolean indicating if the validation is fake (performed when creating a hardfork). */ -ValidateQuery::ValidateQuery(ShardIdFull shard, UnixTime min_ts, BlockIdExt min_masterchain_block_id, - std::vector prev, BlockCandidate candidate, Ref validator_set, +ValidateQuery::ValidateQuery(ShardIdFull shard, BlockIdExt min_masterchain_block_id, std::vector prev, + BlockCandidate candidate, Ref validator_set, td::actor::ActorId manager, td::Timestamp timeout, td::Promise promise, bool is_fake) : shard_(shard) , id_(candidate.id) - , min_ts(min_ts) , min_mc_block_id(min_masterchain_block_id) , prev_blocks(std::move(prev)) , block_candidate(std::move(candidate)) @@ -87,7 +85,6 @@ ValidateQuery::ValidateQuery(ShardIdFull shard, UnixTime min_ts, BlockIdExt min_ , perf_timer_("validateblock", 0.1, [manager](double duration) { send_closure(manager, &ValidatorManager::add_perf_timer_stat, "validateblock", duration); }) { - proc_hash_.zero(); } /** @@ -709,7 +706,7 @@ void ValidateQuery::after_get_latest_mc_state(td::Result> res) { @@ -1333,7 +1330,7 @@ bool ValidateQuery::compute_next_state() { * Unpacks and merges the states of two previous blocks. * Used if the block is after_merge. * Similar to Collator::unpack_merge_last_state() - * + * * @returns True if the unpacking and merging was successful, false otherwise. */ bool ValidateQuery::unpack_merge_prev_state() { @@ -2334,7 +2331,7 @@ bool ValidateQuery::fix_all_processed_upto() { * Adds trivials neighbor after merging two shards. * Trivial neighbors are the two previous blocks. * Almost the same as in Collator. - * + * * @returns True if the operation is successful, false otherwise. */ bool ValidateQuery::add_trivial_neighbor_after_merge() { @@ -2810,7 +2807,7 @@ bool ValidateQuery::precheck_one_account_update(td::ConstBitPtr acc_id, Refsource).blk_.to_str()); } if (unprocessed) { - inbound_queues_empty_ = false; return true; } nb_out_msgs.next(); } - inbound_queues_empty_ = true; return true; } @@ -5777,7 +5772,7 @@ bool ValidateQuery::scan_account_libraries(Ref orig_libs, Ref prev, + ValidateQuery(ShardIdFull shard, BlockIdExt min_masterchain_block_id, std::vector prev, BlockCandidate candidate, td::Ref validator_set, td::actor::ActorId manager, td::Timestamp timeout, td::Promise promise, bool is_fake = false); @@ -127,7 +127,6 @@ class ValidateQuery : public td::actor::Actor { int pending{0}; const ShardIdFull shard_; const BlockIdExt id_; - UnixTime min_ts; BlockIdExt min_mc_block_id; std::vector prev_blocks; std::vector> prev_states; @@ -224,8 +223,7 @@ class ValidateQuery : public td::actor::Actor { td::RefInt256 import_fees_; ton::LogicalTime proc_lt_{0}, claimed_proc_lt_{0}, min_enq_lt_{~0ULL}; - ton::Bits256 proc_hash_, claimed_proc_hash_, min_enq_hash_; - bool inbound_queues_empty_{false}; + ton::Bits256 proc_hash_ = ton::Bits256::zero(), claimed_proc_hash_, min_enq_hash_; std::vector> msg_proc_lt_; std::vector> msg_emitted_lt_; diff --git a/validator/interfaces/db.h b/validator/interfaces/db.h index e0d88e4e7..466203226 100644 --- a/validator/interfaces/db.h +++ b/validator/interfaces/db.h @@ -46,12 +46,12 @@ class Db : public td::actor::Actor { virtual void store_block_candidate(BlockCandidate candidate, td::Promise promise) = 0; virtual void get_block_candidate(ton::PublicKey source, BlockIdExt id, FileHash collated_data_file_hash, td::Promise promise) = 0; + virtual void get_block_candidate_by_block_id(BlockIdExt id, td::Promise promise) = 0; virtual void store_block_state(BlockHandle handle, td::Ref state, td::Promise> promise) = 0; virtual void get_block_state(ConstBlockHandle handle, td::Promise> promise) = 0; virtual void get_cell_db_reader(td::Promise> promise) = 0; - virtual void get_last_deleted_mc_state(td::Promise promise) = 0; virtual void store_persistent_state_file(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::BufferSlice state, td::Promise promise) = 0; diff --git a/validator/interfaces/shard.h b/validator/interfaces/shard.h index 85022e6dd..7a731ab5e 100644 --- a/validator/interfaces/shard.h +++ b/validator/interfaces/shard.h @@ -71,14 +71,15 @@ class MasterchainState : virtual public ShardState { virtual std::vector> get_shards() const = 0; virtual td::Ref get_shard_from_config(ShardIdFull shard) const = 0; virtual bool workchain_is_active(WorkchainId workchain_id) const = 0; + virtual td::uint32 monitor_min_split_depth(WorkchainId workchain_id) const = 0; virtual td::uint32 min_split_depth(WorkchainId workchain_id) const = 0; - virtual td::uint32 soft_min_split_depth(WorkchainId workchain_id) const = 0; virtual BlockSeqno min_ref_masterchain_seqno() const = 0; virtual bool ancestor_is_valid(BlockIdExt id) const = 0; virtual ValidatorSessionConfig get_consensus_config() const = 0; virtual BlockIdExt last_key_block_id() const = 0; virtual BlockIdExt next_key_block_id(BlockSeqno seqno) const = 0; virtual BlockIdExt prev_key_block_id(BlockSeqno seqno) const = 0; + virtual bool is_key_state() const = 0; virtual bool get_old_mc_block_id(ton::BlockSeqno seqno, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt = nullptr) const = 0; virtual bool check_old_mc_block_id(const ton::BlockIdExt& blkid, bool strict = false) const = 0; diff --git a/validator/interfaces/validator-manager.h b/validator/interfaces/validator-manager.h index b6016bc2b..ce0c27e1f 100644 --- a/validator/interfaces/validator-manager.h +++ b/validator/interfaces/validator-manager.h @@ -105,6 +105,8 @@ class ValidatorManager : public ValidatorManagerInterface { virtual void set_block_candidate(BlockIdExt id, BlockCandidate candidate, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, td::Promise promise) = 0; + virtual void send_block_candidate_broadcast(BlockIdExt id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, + td::BufferSlice data) = 0; virtual void wait_block_state_merge(BlockIdExt left_id, BlockIdExt right_id, td::uint32 priority, td::Timestamp timeout, td::Promise> promise) = 0; @@ -144,7 +146,7 @@ class ValidatorManager : public ValidatorManagerInterface { virtual void send_external_message(td::Ref message) = 0; virtual void send_ihr_message(td::Ref message) = 0; virtual void send_top_shard_block_description(td::Ref desc) = 0; - virtual void send_block_broadcast(BlockBroadcast broadcast, bool custom_overlays_only) = 0; + virtual void send_block_broadcast(BlockBroadcast broadcast, int mode) = 0; virtual void update_shard_client_state(BlockIdExt masterchain_block_id, td::Promise promise) = 0; virtual void get_shard_client_state(bool from_db, td::Promise promise) = 0; diff --git a/validator/manager-disk.cpp b/validator/manager-disk.cpp index 5678408c6..62fdc4b43 100644 --- a/validator/manager-disk.cpp +++ b/validator/manager-disk.cpp @@ -128,8 +128,8 @@ void ValidatorManagerImpl::sync_complete(td::Promise promise) { } Ed25519_PublicKey created_by{td::Bits256::zero()}; td::as(created_by.as_bits256().data() + 32 - 4) = ((unsigned)std::time(nullptr) >> 8); - run_collate_query(shard_id, 0, last_masterchain_block_id_, prev, created_by, val_set, td::Ref{true}, - actor_id(this), td::Timestamp::in(10.0), std::move(P)); + run_collate_query(shard_id, last_masterchain_block_id_, prev, created_by, val_set, td::Ref{true}, + actor_id(this), td::Timestamp::in(10.0), std::move(P), td::CancellationToken{}, 0); } void ValidatorManagerImpl::validate_fake(BlockCandidate candidate, std::vector prev, BlockIdExt last, @@ -152,7 +152,7 @@ void ValidatorManagerImpl::validate_fake(BlockCandidate candidate, std::vector promise) { + td::actor::send_closure(db_, &Db::get_block_candidate_by_block_id, id, + promise.wrap([](BlockCandidate &&b) { return std::move(b.data); })); +} + void ValidatorManagerImpl::get_block_proof_from_db(ConstBlockHandle handle, td::Promise> promise) { td::actor::send_closure(db_, &Db::get_block_proof, std::move(handle), std::move(promise)); } @@ -780,6 +785,11 @@ void ValidatorManagerImpl::set_block_candidate(BlockIdExt id, BlockCandidate can td::actor::send_closure(db_, &Db::store_block_candidate, std::move(candidate), std::move(promise)); } +void ValidatorManagerImpl::send_block_candidate_broadcast(BlockIdExt id, CatchainSeqno cc_seqno, + td::uint32 validator_set_hash, td::BufferSlice data) { + callback_->send_block_candidate(id, cc_seqno, validator_set_hash, std::move(data)); +} + void ValidatorManagerImpl::write_handle(BlockHandle handle, td::Promise promise) { td::actor::send_closure(db_, &Db::store_block_handle, std::move(handle), std::move(promise)); } diff --git a/validator/manager-disk.hpp b/validator/manager-disk.hpp index 3a77f2301..5287a3876 100644 --- a/validator/manager-disk.hpp +++ b/validator/manager-disk.hpp @@ -185,6 +185,8 @@ class ValidatorManagerImpl : public ValidatorManager { void set_block_candidate(BlockIdExt id, BlockCandidate candidate, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, td::Promise promise) override; + void send_block_candidate_broadcast(BlockIdExt id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, + td::BufferSlice data) override; void wait_block_state_merge(BlockIdExt left_id, BlockIdExt right_id, td::uint32 priority, td::Timestamp timeout, td::Promise> promise) override; @@ -213,6 +215,7 @@ class ValidatorManagerImpl : public ValidatorManager { void get_shard_state_from_db_short(BlockIdExt block_id, td::Promise> promise) override; void get_block_candidate_from_db(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash, td::Promise promise) override; + void get_candidate_data_by_block_id_from_db(BlockIdExt id, td::Promise promise) override; void get_block_proof_from_db(ConstBlockHandle handle, td::Promise> promise) override; void get_block_proof_from_db_short(BlockIdExt id, td::Promise> promise) override; void get_block_proof_link_from_db(ConstBlockHandle handle, td::Promise> promise) override; @@ -258,7 +261,7 @@ class ValidatorManagerImpl : public ValidatorManager { new_ihr_message(message->serialize()); } void send_top_shard_block_description(td::Ref desc) override; - void send_block_broadcast(BlockBroadcast broadcast, bool custom_overlays_only) override { + void send_block_broadcast(BlockBroadcast broadcast, int mode) override { } void update_shard_client_state(BlockIdExt masterchain_block_id, td::Promise promise) override; @@ -369,6 +372,10 @@ class ValidatorManagerImpl : public ValidatorManager { UNREACHABLE(); } + void prepare_actor_stats(td::Promise promise) override { + UNREACHABLE(); + } + void prepare_perf_timer_stats(td::Promise> promise) override { UNREACHABLE(); } diff --git a/validator/manager-hardfork.cpp b/validator/manager-hardfork.cpp index 49d27085f..91c598aa2 100644 --- a/validator/manager-hardfork.cpp +++ b/validator/manager-hardfork.cpp @@ -415,6 +415,11 @@ void ValidatorManagerImpl::get_block_candidate_from_db(PublicKey source, BlockId td::actor::send_closure(db_, &Db::get_block_candidate, source, id, collated_data_file_hash, std::move(promise)); } +void ValidatorManagerImpl::get_candidate_data_by_block_id_from_db(BlockIdExt id, td::Promise promise) { + td::actor::send_closure(db_, &Db::get_block_candidate_by_block_id, id, + promise.wrap([](BlockCandidate &&b) { return std::move(b.data); })); +} + void ValidatorManagerImpl::get_block_proof_from_db(ConstBlockHandle handle, td::Promise> promise) { td::actor::send_closure(db_, &Db::get_block_proof, std::move(handle), std::move(promise)); } diff --git a/validator/manager-hardfork.hpp b/validator/manager-hardfork.hpp index cf4d3799f..2e703faf7 100644 --- a/validator/manager-hardfork.hpp +++ b/validator/manager-hardfork.hpp @@ -226,6 +226,10 @@ class ValidatorManagerImpl : public ValidatorManager { td::uint32 validator_set_hash, td::Promise promise) override { promise.set_value(td::Unit()); } + void send_block_candidate_broadcast(BlockIdExt id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, + td::BufferSlice data) { + callback_->send_block_candidate(id, cc_seqno, validator_set_hash, std::move(data)); + } void wait_block_state_merge(BlockIdExt left_id, BlockIdExt right_id, td::uint32 priority, td::Timestamp timeout, td::Promise> promise) override; @@ -258,6 +262,7 @@ class ValidatorManagerImpl : public ValidatorManager { void get_shard_state_from_db_short(BlockIdExt block_id, td::Promise> promise) override; void get_block_candidate_from_db(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash, td::Promise promise) override; + void get_candidate_data_by_block_id_from_db(BlockIdExt id, td::Promise promise) override; void get_block_proof_from_db(ConstBlockHandle handle, td::Promise> promise) override; void get_block_proof_from_db_short(BlockIdExt id, td::Promise> promise) override; void get_block_proof_link_from_db(ConstBlockHandle handle, td::Promise> promise) override; @@ -325,7 +330,7 @@ class ValidatorManagerImpl : public ValidatorManager { void send_top_shard_block_description(td::Ref desc) override { UNREACHABLE(); } - void send_block_broadcast(BlockBroadcast broadcast, bool custom_overlays_only) override { + void send_block_broadcast(BlockBroadcast broadcast, int mode) override { } void update_shard_client_state(BlockIdExt masterchain_block_id, td::Promise promise) override { @@ -431,6 +436,10 @@ class ValidatorManagerImpl : public ValidatorManager { UNREACHABLE(); } + void prepare_actor_stats(td::Promise promise) override { + UNREACHABLE(); + } + void prepare_perf_timer_stats(td::Promise> promise) override { UNREACHABLE(); } diff --git a/validator/manager-init.cpp b/validator/manager-init.cpp index aa110380d..64a0a547a 100644 --- a/validator/manager-init.cpp +++ b/validator/manager-init.cpp @@ -227,7 +227,7 @@ void ValidatorManagerMasterchainReiniter::choose_masterchain_state() { } if (!p || ValidatorManager::is_persistent_state(h->unix_time(), p->unix_time())) { auto ttl = ValidatorManager::persistent_state_ttl(h->unix_time()); - double time_to_download = 3600 * 3; + double time_to_download = 3600 * 8; if (ttl > td::Clocks::system() + time_to_download) { handle = h; break; diff --git a/validator/manager.cpp b/validator/manager.cpp index 9058be76a..67cd60a79 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -1035,6 +1035,16 @@ void ValidatorManagerImpl::get_block_candidate_from_db(PublicKey source, BlockId td::actor::send_closure(db_, &Db::get_block_candidate, source, id, collated_data_file_hash, std::move(promise)); } +void ValidatorManagerImpl::get_candidate_data_by_block_id_from_db(BlockIdExt id, td::Promise promise) { + auto it = cached_block_candidates_.find(id); + if (it != cached_block_candidates_.end()) { + promise.set_result(it->second.data.clone()); + return; + } + td::actor::send_closure(db_, &Db::get_block_candidate_by_block_id, id, + promise.wrap([](BlockCandidate &&b) { return std::move(b.data); })); +} + void ValidatorManagerImpl::get_block_proof_from_db(ConstBlockHandle handle, td::Promise> promise) { td::actor::send_closure(db_, &Db::get_block_proof, std::move(handle), std::move(promise)); } @@ -1274,11 +1284,15 @@ void ValidatorManagerImpl::set_block_candidate(BlockIdExt id, BlockCandidate can } if (!id.is_masterchain()) { add_cached_block_candidate(ReceivedBlock{id, candidate.data.clone()}); - callback_->send_block_candidate(id, cc_seqno, validator_set_hash, candidate.data.clone()); } td::actor::send_closure(db_, &Db::store_block_candidate, std::move(candidate), std::move(promise)); } +void ValidatorManagerImpl::send_block_candidate_broadcast(BlockIdExt id, CatchainSeqno cc_seqno, + td::uint32 validator_set_hash, td::BufferSlice data) { + callback_->send_block_candidate(id, cc_seqno, validator_set_hash, std::move(data)); +} + void ValidatorManagerImpl::write_handle(BlockHandle handle, td::Promise promise) { auto P = td::PromiseCreator::lambda( [SelfId = actor_id(this), handle, promise = std::move(promise)](td::Result R) mutable { @@ -1606,12 +1620,13 @@ void ValidatorManagerImpl::send_top_shard_block_description(td::Refsend_broadcast(std::move(broadcast), custom_overlays_only); +void ValidatorManagerImpl::send_block_broadcast(BlockBroadcast broadcast, int mode) { + callback_->send_broadcast(std::move(broadcast), mode); } void ValidatorManagerImpl::start_up() { db_ = create_db_actor(actor_id(this), db_root_, opts_); + actor_stats_ = td::actor::create_actor("actor_stats"); lite_server_cache_ = create_liteserver_cache_actor(actor_id(this), db_root_); token_manager_ = td::actor::create_actor("tokenmanager"); td::mkdir(db_root_ + "/tmp/").ensure(); @@ -2040,15 +2055,13 @@ void ValidatorManagerImpl::update_shards() { auto it2 = next_validator_groups_.find(legacy_val_group_id); if (it2 != next_validator_groups_.end()) { if (!it2->second.actor.empty()) { - td::actor::send_closure(it2->second.actor, &ValidatorGroup::start, prev, last_masterchain_block_id_, - last_masterchain_state_->get_unix_time()); + td::actor::send_closure(it2->second.actor, &ValidatorGroup::start, prev, last_masterchain_block_id_); } new_validator_groups_.emplace(val_group_id, std::move(it2->second)); } else { auto G = create_validator_group(val_group_id, shard, val_set, key_seqno, opts, started_); if (!G.empty()) { - td::actor::send_closure(G, &ValidatorGroup::start, prev, last_masterchain_block_id_, - last_masterchain_state_->get_unix_time()); + td::actor::send_closure(G, &ValidatorGroup::start, prev, last_masterchain_block_id_); } new_validator_groups_.emplace(val_group_id, ValidatorGroupEntry{std::move(G), shard}); } @@ -2057,6 +2070,7 @@ void ValidatorManagerImpl::update_shards() { } } + bool validating_masterchain = false; if (allow_validate_) { for (auto &desc : new_shards) { auto shard = desc.first; @@ -2073,6 +2087,9 @@ void ValidatorManagerImpl::update_shards() { auto validator_id = get_validator(shard, val_set); if (!validator_id.is_zero()) { + if (shard.is_masterchain()) { + validating_masterchain = true; + } auto val_group_id = get_validator_set_id(shard, val_set, opts_hash, key_seqno, opts); if (force_recover) { @@ -2096,15 +2113,13 @@ void ValidatorManagerImpl::update_shards() { auto it2 = next_validator_groups_.find(val_group_id); if (it2 != next_validator_groups_.end()) { if (!it2->second.actor.empty()) { - td::actor::send_closure(it2->second.actor, &ValidatorGroup::start, prev, last_masterchain_block_id_, - last_masterchain_state_->get_unix_time()); + td::actor::send_closure(it2->second.actor, &ValidatorGroup::start, prev, last_masterchain_block_id_); } new_validator_groups_.emplace(val_group_id, std::move(it2->second)); } else { auto G = create_validator_group(val_group_id, shard, val_set, key_seqno, opts, started_); if (!G.empty()) { - td::actor::send_closure(G, &ValidatorGroup::start, prev, last_masterchain_block_id_, - last_masterchain_state_->get_unix_time()); + td::actor::send_closure(G, &ValidatorGroup::start, prev, last_masterchain_block_id_); } new_validator_groups_.emplace(val_group_id, ValidatorGroupEntry{std::move(G), shard}); } @@ -2127,8 +2142,8 @@ void ValidatorManagerImpl::update_shards() { new_next_validator_groups_.emplace(val_group_id, std::move(it->second)); } else { new_next_validator_groups_.emplace( - val_group_id, - ValidatorGroupEntry{create_validator_group(val_group_id, shard, val_set, key_seqno, opts, started_), shard}); + val_group_id, ValidatorGroupEntry{ + create_validator_group(val_group_id, shard, val_set, key_seqno, opts, started_), shard}); } } } @@ -2168,11 +2183,9 @@ void ValidatorManagerImpl::update_shards() { }); td::actor::send_closure(db_, &Db::update_destroyed_validator_sessions, gc_list_, std::move(P)); } - if (!serializer_.empty()) { - td::actor::send_closure( - serializer_, &AsyncStateSerializer::auto_disable_serializer, - !validator_groups_.empty() && last_masterchain_state_->get_global_id() == -239); // mainnet only + td::actor::send_closure(serializer_, &AsyncStateSerializer::auto_disable_serializer, + is_validator() && last_masterchain_state_->get_global_id() == -239); // mainnet only } } @@ -2753,6 +2766,10 @@ void ValidatorManagerImpl::send_peek_key_block_request() { send_get_next_key_blocks_request(last_known_key_block_handle_->id(), 1, std::move(P)); } +void ValidatorManagerImpl::prepare_actor_stats(td::Promise promise) { + send_closure(actor_stats_, &td::actor::ActorStats::prepare_stats, std::move(promise)); +} + void ValidatorManagerImpl::prepare_stats(td::Promise>> promise) { auto merger = StatsMerger::create(std::move(promise)); @@ -2767,24 +2784,10 @@ void ValidatorManagerImpl::prepare_stats(td::Promiseid().seqno()](td::Result R) mutable { - TRY_RESULT_PROMISE(promise, seqno, std::move(R)); - std::string s; - if (seqno == 0) { - s = "none"; - } else if (seqno <= gc_seqno) { - s = PSTRING() << seqno << " (gc_seqno-" << (gc_seqno - seqno) << ")"; - } else { - s = PSTRING() << seqno << " (gc_seqno+" << (seqno - gc_seqno) << ")"; - } - std::vector> vec; - vec.emplace_back("lastgcdmasterchainstate", std::move(s)); - promise.set_value(std::move(vec)); - }); } + td::NamedThreadSafeCounter::get_default().for_each([&](auto key, auto value) { + vec.emplace_back("counter." + key, PSTRING() << value); + }); if (!shard_client_.empty()) { auto P = td::PromiseCreator::lambda([promise = merger.make_promise("")](td::Result R) mutable { @@ -3008,18 +3011,18 @@ void ValidatorManagerImpl::get_block_state_for_litequery(BlockIdExt block_id, promise.set_result(R.move_as_ok()); return; } - td::actor::send_closure(manager, &ValidatorManagerImpl::get_block_handle_for_litequery, - block_id, [manager, promise = std::move(promise)](td::Result R) mutable { - TRY_RESULT_PROMISE(promise, handle, std::move(R)); - td::actor::send_closure_later(manager, &ValidatorManager::get_shard_state_from_db, std::move(handle), - std::move(promise)); - }); + td::actor::send_closure(manager, &ValidatorManagerImpl::get_block_handle_for_litequery, block_id, + [manager, promise = std::move(promise)](td::Result R) mutable { + TRY_RESULT_PROMISE(promise, handle, std::move(R)); + td::actor::send_closure_later(manager, &ValidatorManager::get_shard_state_from_db, + std::move(handle), std::move(promise)); + }); }); } } void ValidatorManagerImpl::get_block_by_lt_for_litequery(AccountIdPrefixFull account, LogicalTime lt, - td::Promise promise) { + td::Promise promise) { get_block_by_lt_from_db( account, lt, [=, SelfId = actor_id(this), promise = std::move(promise)](td::Result R) mutable { if (R.is_ok() && R.ok()->is_applied()) { @@ -3032,7 +3035,7 @@ void ValidatorManagerImpl::get_block_by_lt_for_litequery(AccountIdPrefixFull acc } void ValidatorManagerImpl::get_block_by_unix_time_for_litequery(AccountIdPrefixFull account, UnixTime ts, - td::Promise promise) { + td::Promise promise) { get_block_by_unix_time_from_db( account, ts, [=, SelfId = actor_id(this), promise = std::move(promise)](td::Result R) mutable { if (R.is_ok() && R.ok()->is_applied()) { @@ -3045,7 +3048,7 @@ void ValidatorManagerImpl::get_block_by_unix_time_for_litequery(AccountIdPrefixF } void ValidatorManagerImpl::get_block_by_seqno_for_litequery(AccountIdPrefixFull account, BlockSeqno seqno, - td::Promise promise) { + td::Promise promise) { get_block_by_seqno_from_db( account, seqno, [=, SelfId = actor_id(this), promise = std::move(promise)](td::Result R) mutable { diff --git a/validator/manager.hpp b/validator/manager.hpp index 99aa4e0e1..7410f75f1 100644 --- a/validator/manager.hpp +++ b/validator/manager.hpp @@ -21,6 +21,7 @@ #include "common/refcnt.hpp" #include "interfaces/validator-manager.h" #include "interfaces/db.h" +#include "td/actor/ActorStats.h" #include "td/actor/PromiseFuture.h" #include "td/utils/SharedSlice.h" #include "td/utils/buffer.h" @@ -433,6 +434,8 @@ class ValidatorManagerImpl : public ValidatorManager { void set_block_candidate(BlockIdExt id, BlockCandidate candidate, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, td::Promise promise) override; + void send_block_candidate_broadcast(BlockIdExt id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, + td::BufferSlice data) override; void wait_block_state_merge(BlockIdExt left_id, BlockIdExt right_id, td::uint32 priority, td::Timestamp timeout, td::Promise> promise) override; @@ -460,6 +463,7 @@ class ValidatorManagerImpl : public ValidatorManager { void get_shard_state_from_db_short(BlockIdExt block_id, td::Promise> promise) override; void get_block_candidate_from_db(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash, td::Promise promise) override; + void get_candidate_data_by_block_id_from_db(BlockIdExt id, td::Promise promise) override; void get_block_proof_from_db(ConstBlockHandle handle, td::Promise> promise) override; void get_block_proof_from_db_short(BlockIdExt id, td::Promise> promise) override; void get_block_proof_link_from_db(ConstBlockHandle handle, td::Promise> promise) override; @@ -496,7 +500,7 @@ class ValidatorManagerImpl : public ValidatorManager { void send_external_message(td::Ref message) override; void send_ihr_message(td::Ref message) override; void send_top_shard_block_description(td::Ref desc) override; - void send_block_broadcast(BlockBroadcast broadcast, bool custom_overlays_only) override; + void send_block_broadcast(BlockBroadcast broadcast, int mode) override; void update_shard_client_state(BlockIdExt masterchain_block_id, td::Promise promise) override; void get_shard_client_state(bool from_db, td::Promise promise) override; @@ -581,6 +585,8 @@ class ValidatorManagerImpl : public ValidatorManager { void prepare_stats(td::Promise>> promise) override; + void prepare_actor_stats(td::Promise promise) override; + void prepare_perf_timer_stats(td::Promise> promise) override; void add_perf_timer_stat(std::string name, double duration) override; @@ -677,6 +683,7 @@ class ValidatorManagerImpl : public ValidatorManager { private: std::unique_ptr callback_; td::actor::ActorOwn db_; + td::actor::ActorOwn actor_stats_; bool started_ = false; bool allow_validate_ = false; diff --git a/validator/net/download-block-new.cpp b/validator/net/download-block-new.cpp index 9ec36e335..e9a193b46 100644 --- a/validator/net/download-block-new.cpp +++ b/validator/net/download-block-new.cpp @@ -202,10 +202,10 @@ void DownloadBlockNew::got_node_to_download(adnl::AdnlNodeIdShort node) { } if (client_.empty()) { td::actor::send_closure(overlays_, &overlay::Overlays::send_query_via, download_from_, local_id_, overlay_id_, - "get_proof", std::move(P), td::Timestamp::in(15.0), std::move(q), + "get_block_full", std::move(P), td::Timestamp::in(15.0), std::move(q), FullNode::max_proof_size() + FullNode::max_block_size() + 128, rldp_); } else { - td::actor::send_closure(client_, &adnl::AdnlExtClient::send_query, "get_prepare", + td::actor::send_closure(client_, &adnl::AdnlExtClient::send_query, "get_block_full", create_serialize_tl_object_suffix(std::move(q)), td::Timestamp::in(15.0), std::move(P)); } diff --git a/validator/net/download-state.cpp b/validator/net/download-state.cpp index 2740ce412..2b373ef37 100644 --- a/validator/net/download-state.cpp +++ b/validator/net/download-state.cpp @@ -52,12 +52,7 @@ DownloadState::DownloadState(BlockIdExt block_id, BlockIdExt masterchain_block_i void DownloadState::abort_query(td::Status reason) { if (promise_) { - if (reason.code() == ErrorCode::notready || reason.code() == ErrorCode::timeout) { - VLOG(FULL_NODE_DEBUG) << "failed to download state " << block_id_ << " from " << download_from_ << ": " << reason; - } else { - VLOG(FULL_NODE_NOTICE) << "failed to download state " << block_id_ << " from " << download_from_ << ": " - << reason; - } + LOG(WARNING) << "failed to download state " << block_id_.to_str() << " from " << download_from_ << ": " << reason; promise_.set_error(std::move(reason)); } stop(); @@ -77,6 +72,19 @@ void DownloadState::finish_query() { void DownloadState::start_up() { alarm_timestamp() = timeout_; + td::actor::send_closure(validator_manager_, &ValidatorManagerInterface::get_persistent_state, block_id_, + masterchain_block_id_, + [SelfId = actor_id(this), block_id = block_id_](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &DownloadState::get_block_handle); + } else { + LOG(WARNING) << "got block state from disk: " << block_id.to_str(); + td::actor::send_closure(SelfId, &DownloadState::got_block_state, R.move_as_ok()); + } + }); +} + +void DownloadState::get_block_handle() { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { td::actor::send_closure(SelfId, &DownloadState::abort_query, R.move_as_error()); @@ -115,7 +123,7 @@ void DownloadState::got_block_handle(BlockHandle handle) { void DownloadState::got_node_to_download(adnl::AdnlNodeIdShort node) { download_from_ = node; - LOG(INFO) << "downloading state " << block_id_.to_str() << " from " << download_from_; + LOG(WARNING) << "downloading state " << block_id_.to_str() << " from " << download_from_; auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) mutable { if (R.is_error()) { @@ -192,8 +200,8 @@ void DownloadState::got_block_state_part(td::BufferSlice data, td::uint32 reques double elapsed = prev_logged_timer_.elapsed(); if (elapsed > 10.0) { prev_logged_timer_ = td::Timer(); - LOG(INFO) << "downloading state " << block_id_.to_str() << ": total=" << sum_ << " (" - << td::format::as_size((td::uint64)(double(sum_ - prev_logged_sum_) / elapsed)) << "/s)"; + LOG(WARNING) << "downloading state " << block_id_.to_str() << ": " << td::format::as_size(sum_) << " (" + << td::format::as_size((td::uint64)(double(sum_ - prev_logged_sum_) / elapsed)) << "/s)"; prev_logged_sum_ = sum_; } @@ -234,7 +242,7 @@ void DownloadState::got_block_state_part(td::BufferSlice data, td::uint32 reques void DownloadState::got_block_state(td::BufferSlice data) { state_ = std::move(data); - LOG(INFO) << "finished downloading state " << block_id_.to_str() << ": total=" << sum_; + LOG(WARNING) << "finished downloading state " << block_id_.to_str() << ": " << td::format::as_size(state_.size()); finish_query(); } diff --git a/validator/net/download-state.hpp b/validator/net/download-state.hpp index 7db1327f8..19c44beb3 100644 --- a/validator/net/download-state.hpp +++ b/validator/net/download-state.hpp @@ -43,6 +43,7 @@ class DownloadState : public td::actor::Actor { void finish_query(); void start_up() override; + void get_block_handle(); void got_block_handle(BlockHandle handle); void got_node_to_download(adnl::AdnlNodeIdShort node); void got_block_state_description(td::BufferSlice data_description); diff --git a/validator/shard-client.cpp b/validator/shard-client.cpp index 8fad36122..24dd77e87 100644 --- a/validator/shard-client.cpp +++ b/validator/shard-client.cpp @@ -79,30 +79,30 @@ void ShardClient::got_init_state_from_db(td::Ref state) { void ShardClient::start_up_init_mode() { build_shard_overlays(); - - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { - R.ensure(); - td::actor::send_closure(SelfId, &ShardClient::applied_all_shards); - }); - - td::MultiPromise mp; - auto ig = mp.init_guard(); - ig.add_promise(std::move(P)); - - auto vec = masterchain_state_->get_shards(); - for (auto &shard : vec) { - if (opts_->need_monitor(shard->shard())) { - auto P = td::PromiseCreator::lambda([promise = ig.get_promise()](td::Result> R) mutable { - R.ensure(); - promise.set_value(td::Unit()); - }); - - td::actor::create_actor("downloadstate", shard->top_block_id(), - masterchain_block_handle_->id(), 2, manager_, - td::Timestamp::in(3600 * 3), std::move(P)) - .release(); + std::vector shards; + for (const auto& s : masterchain_state_->get_shards()) { + if (opts_->need_monitor(s->shard())) { + shards.push_back(s->top_block_id()); } } + download_shard_states(masterchain_block_handle_->id(), std::move(shards), 0); +} + +void ShardClient::download_shard_states(BlockIdExt masterchain_block_id, std::vector shards, size_t idx) { + if (idx >= shards.size()) { + LOG(WARNING) << "downloaded all shard states"; + applied_all_shards(); + return; + } + BlockIdExt block_id = shards[idx]; + td::actor::create_actor( + "downloadstate", block_id, masterchain_block_handle_->id(), 2, manager_, td::Timestamp::in(3600 * 5), + [=, SelfId = actor_id(this), shards = std::move(shards)](td::Result> R) { + R.ensure(); + td::actor::send_closure(SelfId, &ShardClient::download_shard_states, masterchain_block_id, std::move(shards), + idx + 1); + }) + .release(); } void ShardClient::applied_all_shards() { @@ -250,7 +250,7 @@ void ShardClient::build_shard_overlays() { for (auto &x : v) { auto shard = x->shard(); if (opts_->need_monitor(shard)) { - auto d = masterchain_state_->soft_min_split_depth(shard.workchain); + auto d = masterchain_state_->monitor_min_split_depth(shard.workchain); auto l = shard_prefix_length(shard.shard); if (l > d) { shard = shard_prefix(shard, d); diff --git a/validator/shard-client.hpp b/validator/shard-client.hpp index 455b67e7c..c1676debd 100644 --- a/validator/shard-client.hpp +++ b/validator/shard-client.hpp @@ -68,19 +68,12 @@ class ShardClient : public td::actor::Actor { void start_up() override; void start_up_init_mode(); - void start_up_init_mode_finished(); + void download_shard_states(BlockIdExt masterchain_block_id, std::vector shards, size_t idx); void start(); void got_state_from_db(BlockIdExt masterchain_block_id); void got_init_handle_from_db(BlockHandle handle); void got_init_state_from_db(td::Ref state); - void im_download_shard_state(BlockIdExt block_id, td::Promise promise); - void im_downloaded_zero_state(BlockIdExt block_id, td::BufferSlice data, td::Promise promise); - void im_downloaded_proof_link(BlockIdExt block_id, td::BufferSlice data, td::Promise promise); - void im_checked_proof_link(BlockIdExt block_id, td::Promise promise); - void im_downloaded_shard_state(BlockIdExt block_id, td::Promise promise); - void im_got_shard_handle(BlockHandle handle, td::Promise promise); - void new_masterchain_block_id(BlockIdExt masterchain_block_id); void got_masterchain_block_handle(BlockHandle handle); void download_masterchain_state(); diff --git a/validator/state-serializer.cpp b/validator/state-serializer.cpp index 516d8177f..ab38a6e99 100644 --- a/validator/state-serializer.cpp +++ b/validator/state-serializer.cpp @@ -22,6 +22,7 @@ #include "ton/ton-io.hpp" #include "common/delay.h" #include "td/utils/filesystem.h" +#include "td/utils/HashSet.h" namespace ton { @@ -227,17 +228,17 @@ void AsyncStateSerializer::got_masterchain_handle(BlockHandle handle) { class CachedCellDbReader : public vm::CellDbReader { public: CachedCellDbReader(std::shared_ptr parent, - std::shared_ptr>> cache) + std::shared_ptr cache) : parent_(std::move(parent)), cache_(std::move(cache)) { } td::Result> load_cell(td::Slice hash) override { ++total_reqs_; DCHECK(hash.size() == 32); if (cache_) { - auto it = cache_->find(td::Bits256{(const unsigned char*)hash.data()}); + auto it = cache_->find(hash); if (it != cache_->end()) { ++cached_reqs_; - TRY_RESULT(loaded_cell, it->second->load_cell()); + TRY_RESULT(loaded_cell, (*it)->load_cell()); return loaded_cell.data_cell; } } @@ -248,7 +249,7 @@ class CachedCellDbReader : public vm::CellDbReader { } private: std::shared_ptr parent_; - std::shared_ptr>> cache_; + std::shared_ptr cache_; td::uint64 total_reqs_ = 0; td::uint64 cached_reqs_ = 0; @@ -272,10 +273,9 @@ void AsyncStateSerializer::PreviousStateCache::prepare_cache(ShardIdFull shard) td::Timer timer; LOG(WARNING) << "Preloading previous persistent state for shard " << shard.to_str() << " (" << cur_shards.size() << " files)"; - std::map> cells; + vm::CellHashSet cells; std::function)> dfs = [&](td::Ref cell) { - td::Bits256 hash = cell->get_hash().bits(); - if (!cells.emplace(hash, cell).second) { + if (!cells.insert(cell).second) { return; } bool is_special; @@ -303,7 +303,7 @@ void AsyncStateSerializer::PreviousStateCache::prepare_cache(ShardIdFull shard) dfs(r_root.move_as_ok()); } LOG(WARNING) << "Preloaded previous state: " << cells.size() << " cells in " << timer.elapsed() << "s"; - cache = std::make_shared>>(std::move(cells)); + cache = std::make_shared(std::move(cells)); } void AsyncStateSerializer::got_masterchain_state(td::Ref state, @@ -322,15 +322,18 @@ void AsyncStateSerializer::got_masterchain_state(td::Ref state shards_.push_back(v->top_block_id()); } - auto write_data = [shard = state->get_shard(), hash = state->root_cell()->get_hash(), cell_db_reader, + auto write_data = [shard = state->get_shard(), root = state->root_cell(), cell_db_reader, previous_state_cache = previous_state_cache_, fast_serializer_enabled = opts_->get_fast_state_serializer_enabled(), cancellation_token = cancellation_token_source_.get_cancellation_token()](td::FileFd& fd) mutable { + if (!cell_db_reader) { + return vm::std_boc_serialize_to_file(root, fd, 31, std::move(cancellation_token)); + } if (fast_serializer_enabled) { previous_state_cache->prepare_cache(shard); } auto new_cell_db_reader = std::make_shared(cell_db_reader, previous_state_cache->cache); - auto res = vm::std_boc_serialize_to_file_large(new_cell_db_reader, hash, fd, 31, std::move(cancellation_token)); + auto res = vm::std_boc_serialize_to_file_large(new_cell_db_reader, root->get_hash(), fd, 31, std::move(cancellation_token)); new_cell_db_reader->print_stats(); return res; }; @@ -384,15 +387,18 @@ void AsyncStateSerializer::got_shard_state(BlockHandle handle, td::Refid().id.to_str(); - auto write_data = [shard = state->get_shard(), hash = state->root_cell()->get_hash(), cell_db_reader, + auto write_data = [shard = state->get_shard(), root = state->root_cell(), cell_db_reader, previous_state_cache = previous_state_cache_, fast_serializer_enabled = opts_->get_fast_state_serializer_enabled(), cancellation_token = cancellation_token_source_.get_cancellation_token()](td::FileFd& fd) mutable { + if (!cell_db_reader) { + return vm::std_boc_serialize_to_file(root, fd, 31, std::move(cancellation_token)); + } if (fast_serializer_enabled) { previous_state_cache->prepare_cache(shard); } auto new_cell_db_reader = std::make_shared(cell_db_reader, previous_state_cache->cache); - auto res = vm::std_boc_serialize_to_file_large(new_cell_db_reader, hash, fd, 31, std::move(cancellation_token)); + auto res = vm::std_boc_serialize_to_file_large(new_cell_db_reader, root->get_hash(), fd, 31, std::move(cancellation_token)); new_cell_db_reader->print_stats(); return res; }; diff --git a/validator/state-serializer.hpp b/validator/state-serializer.hpp index 68606d1ea..b38a216b7 100644 --- a/validator/state-serializer.hpp +++ b/validator/state-serializer.hpp @@ -51,7 +51,7 @@ class AsyncStateSerializer : public td::actor::Actor { std::vector shards_; struct PreviousStateCache { std::vector> state_files; - std::shared_ptr>> cache; + std::shared_ptr cache; std::vector cur_shards; void prepare_cache(ShardIdFull shard); diff --git a/validator/validator-group.cpp b/validator/validator-group.cpp index bd545f607..1817180db 100644 --- a/validator/validator-group.cpp +++ b/validator/validator-group.cpp @@ -18,6 +18,7 @@ */ #include "validator-group.hpp" #include "fabric.h" +#include "full-node-master.hpp" #include "ton/ton-io.hpp" #include "td/utils/overloaded.h" #include "common/delay.h" @@ -27,8 +28,14 @@ namespace ton { namespace validator { +static bool need_send_candidate_broadcast(const validatorsession::BlockSourceInfo &source_info, bool is_masterchain) { + return source_info.first_block_round == source_info.round && source_info.source_priority == 0 && !is_masterchain; +} + void ValidatorGroup::generate_block_candidate( - td::uint32 round_id, td::Promise promise) { + validatorsession::BlockSourceInfo source_info, + td::Promise promise) { + td::uint32 round_id = source_info.round; if (round_id > last_known_round_id_) { last_known_round_id_ = round_id; } @@ -51,14 +58,17 @@ void ValidatorGroup::generate_block_candidate( return validatorsession::ValidatorSession::GeneratedCandidate{std::move(res), false}; })); run_collate_query( - shard_, min_ts_, min_masterchain_block_id_, prev_block_ids_, - Ed25519_PublicKey{local_id_full_.ed25519_value().raw()}, validator_set_, opts_->get_collator_options(), manager_, - td::Timestamp::in(10.0), [SelfId = actor_id(this), cache = cached_collated_block_](td::Result R) { - td::actor::send_closure(SelfId, &ValidatorGroup::generated_block_candidate, std::move(cache), std::move(R)); - }); + shard_, min_masterchain_block_id_, prev_block_ids_, Ed25519_PublicKey{local_id_full_.ed25519_value().raw()}, + validator_set_, opts_->get_collator_options(), manager_, td::Timestamp::in(10.0), + [SelfId = actor_id(this), cache = cached_collated_block_, source_info](td::Result R) mutable { + td::actor::send_closure(SelfId, &ValidatorGroup::generated_block_candidate, std::move(source_info), + std::move(cache), std::move(R)); + }, cancellation_token_source_.get_cancellation_token(), /* mode = */ 0); } -void ValidatorGroup::generated_block_candidate(std::shared_ptr cache, td::Result R) { +void ValidatorGroup::generated_block_candidate(validatorsession::BlockSourceInfo source_info, + std::shared_ptr cache, + td::Result R) { if (R.is_error()) { for (auto &p : cache->promises) { p.set_error(R.error().clone()); @@ -69,6 +79,9 @@ void ValidatorGroup::generated_block_candidate(std::shared_ptrresult = std::move(candidate); for (auto &p : cache->promises) { p.set_value(cache->result.value().clone()); @@ -77,8 +90,9 @@ void ValidatorGroup::generated_block_candidate(std::shared_ptrpromises.clear(); } -void ValidatorGroup::validate_block_candidate(td::uint32 round_id, BlockCandidate block, +void ValidatorGroup::validate_block_candidate(validatorsession::BlockSourceInfo source_info, BlockCandidate block, td::Promise> promise) { + td::uint32 round_id = source_info.round; if (round_id > last_known_round_id_) { last_known_round_id_ = round_id; } @@ -97,7 +111,8 @@ void ValidatorGroup::validate_block_candidate(td::uint32 round_id, BlockCandidat return; } - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), round_id, block = block.clone(), + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), source_info, block = block.clone(), manager = manager_, + validator_set = validator_set_, promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { auto S = R.move_as_error(); @@ -105,19 +120,22 @@ void ValidatorGroup::validate_block_candidate(td::uint32 round_id, BlockCandidat LOG(ERROR) << "failed to validate candidate: " << S; } delay_action( - [SelfId, round_id, block = std::move(block), promise = std::move(promise)]() mutable { - td::actor::send_closure(SelfId, &ValidatorGroup::validate_block_candidate, round_id, std::move(block), - std::move(promise)); + [SelfId, source_info, block = std::move(block), promise = std::move(promise)]() mutable { + td::actor::send_closure(SelfId, &ValidatorGroup::validate_block_candidate, std::move(source_info), + std::move(block), std::move(promise)); }, td::Timestamp::in(0.1)); } else { auto v = R.move_as_ok(); v.visit(td::overloaded( [&](UnixTime ts) { - td::actor::send_closure(SelfId, &ValidatorGroup::update_approve_cache, block_to_cache_key(block), - ts); + td::actor::send_closure(SelfId, &ValidatorGroup::update_approve_cache, block_to_cache_key(block), ts); td::actor::send_closure(SelfId, &ValidatorGroup::add_available_block_candidate, block.pubkey.as_bits256(), block.id, block.collated_file_hash); + if (need_send_candidate_broadcast(source_info, block.id.is_masterchain())) { + td::actor::send_closure(SelfId, &ValidatorGroup::send_block_candidate_broadcast, block.id, + block.data.clone()); + } promise.set_value({ts, false}); }, [&](CandidateReject reject) { @@ -132,21 +150,22 @@ void ValidatorGroup::validate_block_candidate(td::uint32 round_id, BlockCandidat } VLOG(VALIDATOR_DEBUG) << "validating block candidate " << next_block_id; block.id = next_block_id; - run_validate_query(shard_, min_ts_, min_masterchain_block_id_, prev_block_ids_, std::move(block), validator_set_, - manager_, td::Timestamp::in(15.0), std::move(P)); + run_validate_query(shard_, min_masterchain_block_id_, prev_block_ids_, std::move(block), validator_set_, manager_, + td::Timestamp::in(15.0), std::move(P)); } void ValidatorGroup::update_approve_cache(CacheKey key, UnixTime value) { approved_candidates_cache_[key] = value; } -void ValidatorGroup::accept_block_candidate(td::uint32 round_id, PublicKeyHash src, td::BufferSlice block_data, +void ValidatorGroup::accept_block_candidate(validatorsession::BlockSourceInfo source_info, td::BufferSlice block_data, RootHash root_hash, FileHash file_hash, std::vector signatures, std::vector approve_signatures, validatorsession::ValidatorSessionStats stats, td::Promise promise) { stats.cc_seqno = validator_set_->get_catchain_seqno(); + td::uint32 round_id = source_info.round; if (round_id >= last_known_round_id_) { last_known_round_id_ = round_id + 1; } @@ -165,7 +184,21 @@ void ValidatorGroup::accept_block_candidate(td::uint32 round_id, PublicKeyHash s td::actor::send_closure(manager_, &ValidatorManager::log_validator_session_stats, next_block_id, std::move(stats)); auto block = block_data.size() > 0 ? create_block(next_block_id, std::move(block_data)).move_as_ok() : td::Ref{}; - bool send_broadcast = src == local_id_; + + // Creator of the block sends broadcast to public overlays + // Creator of the block sends broadcast to private block overlay unless candidate broadcast was sent + // Any node sends broadcast to custom overlays unless candidate broadcast was sent + int send_broadcast_mode = 0; + bool sent_candidate = sent_candidate_broadcasts_.contains(next_block_id); + if (source_info.source.compute_short_id() == local_id_) { + send_broadcast_mode |= fullnode::FullNode::broadcast_mode_public; + if (!sent_candidate) { + send_broadcast_mode |= fullnode::FullNode::broadcast_mode_private_block; + } + } + if (!sent_candidate) { + send_broadcast_mode |= fullnode::FullNode::broadcast_mode_custom; + } auto P = td::PromiseCreator::lambda([=, SelfId = actor_id(this), block_id = next_block_id, prev = prev_block_ids_, promise = std::move(promise)](td::Result R) mutable { @@ -176,7 +209,7 @@ void ValidatorGroup::accept_block_candidate(td::uint32 round_id, PublicKeyHash s } LOG_CHECK(R.error().code() == ErrorCode::timeout || R.error().code() == ErrorCode::notready) << R.move_as_error(); td::actor::send_closure(SelfId, &ValidatorGroup::retry_accept_block_query, block_id, std::move(block), - std::move(prev), std::move(sig_set), std::move(approve_sig_set), send_broadcast, + std::move(prev), std::move(sig_set), std::move(approve_sig_set), send_broadcast_mode, std::move(promise)); } else { promise.set_value(R.move_as_ok()); @@ -184,22 +217,24 @@ void ValidatorGroup::accept_block_candidate(td::uint32 round_id, PublicKeyHash s }); run_accept_block_query(next_block_id, std::move(block), prev_block_ids_, validator_set_, std::move(sig_set), - std::move(approve_sig_set), send_broadcast, manager_, std::move(P)); + std::move(approve_sig_set), send_broadcast_mode, manager_, + std::move(P)); prev_block_ids_ = std::vector{next_block_id}; cached_collated_block_ = nullptr; approved_candidates_cache_.clear(); + cancellation_token_source_.cancel(); } void ValidatorGroup::retry_accept_block_query(BlockIdExt block_id, td::Ref block, std::vector prev, td::Ref sig_set, - td::Ref approve_sig_set, bool send_broadcast, + td::Ref approve_sig_set, int send_broadcast_mode, td::Promise promise) { auto P = td::PromiseCreator::lambda( [=, SelfId = actor_id(this), promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { LOG_CHECK(R.error().code() == ErrorCode::timeout) << R.move_as_error(); td::actor::send_closure(SelfId, &ValidatorGroup::retry_accept_block_query, block_id, std::move(block), - std::move(prev), std::move(sig_set), std::move(approve_sig_set), send_broadcast, + std::move(prev), std::move(sig_set), std::move(approve_sig_set), send_broadcast_mode, std::move(promise)); } else { promise.set_value(R.move_as_ok()); @@ -207,7 +242,7 @@ void ValidatorGroup::retry_accept_block_query(BlockIdExt block_id, td::Ref ValidatorGroup::ma public: Callback(td::actor::ActorId id) : id_(id) { } - void on_candidate(td::uint32 round, PublicKey source, validatorsession::ValidatorSessionRootHash root_hash, - td::BufferSlice data, td::BufferSlice collated_data, + void on_candidate(validatorsession::BlockSourceInfo source_info, + validatorsession::ValidatorSessionRootHash root_hash, td::BufferSlice data, + td::BufferSlice collated_data, td::Promise promise) override { auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result> R) mutable { @@ -265,18 +301,19 @@ std::unique_ptr ValidatorGroup::ma } }); - BlockCandidate candidate{Ed25519_PublicKey{source.ed25519_value().raw()}, + BlockCandidate candidate{Ed25519_PublicKey{source_info.source.ed25519_value().raw()}, BlockIdExt{0, 0, 0, root_hash, sha256_bits256(data.as_slice())}, sha256_bits256(collated_data.as_slice()), data.clone(), collated_data.clone()}; - td::actor::send_closure(id_, &ValidatorGroup::validate_block_candidate, round, std::move(candidate), - std::move(P)); + td::actor::send_closure(id_, &ValidatorGroup::validate_block_candidate, std::move(source_info), + std::move(candidate), std::move(P)); } - void on_generate_slot(td::uint32 round, + void on_generate_slot(validatorsession::BlockSourceInfo source_info, td::Promise promise) override { - td::actor::send_closure(id_, &ValidatorGroup::generate_block_candidate, round, std::move(promise)); + td::actor::send_closure(id_, &ValidatorGroup::generate_block_candidate, std::move(source_info), + std::move(promise)); } - void on_block_committed(td::uint32 round, PublicKey source, validatorsession::ValidatorSessionRootHash root_hash, + void on_block_committed(validatorsession::BlockSourceInfo source_info, validatorsession::ValidatorSessionRootHash root_hash, validatorsession::ValidatorSessionFileHash file_hash, td::BufferSlice data, std::vector> signatures, std::vector> approve_signatures, @@ -290,9 +327,9 @@ std::unique_ptr ValidatorGroup::ma approve_sigs.emplace_back(BlockSignature{sig.first.bits256_value(), std::move(sig.second)}); } auto P = td::PromiseCreator::lambda([](td::Result) {}); - td::actor::send_closure(id_, &ValidatorGroup::accept_block_candidate, round, source.compute_short_id(), - std::move(data), root_hash, file_hash, std::move(sigs), std::move(approve_sigs), - std::move(stats), std::move(P)); + td::actor::send_closure(id_, &ValidatorGroup::accept_block_candidate, std::move(source_info), std::move(data), + root_hash, file_hash, std::move(sigs), std::move(approve_sigs), std::move(stats), + std::move(P)); } void on_block_skipped(td::uint32 round) override { td::actor::send_closure(id_, &ValidatorGroup::skip_round, round); @@ -359,10 +396,9 @@ void ValidatorGroup::create_session() { } } -void ValidatorGroup::start(std::vector prev, BlockIdExt min_masterchain_block_id, UnixTime min_ts) { +void ValidatorGroup::start(std::vector prev, BlockIdExt min_masterchain_block_id) { prev_block_ids_ = prev; min_masterchain_block_id_ = min_masterchain_block_id; - min_ts_ = min_ts; cached_collated_block_ = nullptr; approved_candidates_cache_.clear(); started_ = true; @@ -379,7 +415,7 @@ void ValidatorGroup::start(std::vector prev, BlockIdExt min_masterch auto block = p.block.size() > 0 ? create_block(next_block_id, std::move(p.block)).move_as_ok() : td::Ref{}; retry_accept_block_query(next_block_id, std::move(block), prev_block_ids_, std::move(p.sigs), - std::move(p.approve_sigs), false, std::move(p.promise)); + std::move(p.approve_sigs), 0, std::move(p.promise)); prev_block_ids_ = std::vector{next_block_id}; } postponed_accept_.clear(); @@ -434,6 +470,7 @@ void ValidatorGroup::destroy() { delay_action([ses]() mutable { td::actor::send_closure(ses, &validatorsession::ValidatorSession::destroy); }, td::Timestamp::in(10.0)); } + cancellation_token_source_.cancel(); stop(); } diff --git a/validator/validator-group.hpp b/validator/validator-group.hpp index 936d2fdc7..db55614f4 100644 --- a/validator/validator-group.hpp +++ b/validator/validator-group.hpp @@ -34,24 +34,24 @@ class ValidatorManager; class ValidatorGroup : public td::actor::Actor { public: - void generate_block_candidate(td::uint32 round_id, + void generate_block_candidate(validatorsession::BlockSourceInfo source_info, td::Promise promise); - void validate_block_candidate(td::uint32 round_id, BlockCandidate block, + void validate_block_candidate(validatorsession::BlockSourceInfo source_info, BlockCandidate block, td::Promise> promise); - void accept_block_candidate(td::uint32 round_id, PublicKeyHash src, td::BufferSlice block, RootHash root_hash, + void accept_block_candidate(validatorsession::BlockSourceInfo source_info, td::BufferSlice block, RootHash root_hash, FileHash file_hash, std::vector signatures, std::vector approve_signatures, validatorsession::ValidatorSessionStats stats, td::Promise promise); void skip_round(td::uint32 round); void retry_accept_block_query(BlockIdExt block_id, td::Ref block, std::vector prev, td::Ref sigs, td::Ref approve_sigs, - bool send_broadcast, td::Promise promise); + int send_broadcast_mode, td::Promise promise); void get_approved_candidate(PublicKey source, RootHash root_hash, FileHash file_hash, FileHash collated_data_file_hash, td::Promise promise); BlockIdExt create_next_block_id(RootHash root_hash, FileHash file_hash) const; BlockId create_next_block_id_simple() const; - void start(std::vector prev, BlockIdExt min_masterchain_block_id, UnixTime min_ts); + void start(std::vector prev, BlockIdExt min_masterchain_block_id); void create_session(); void destroy(); void start_up() override { @@ -114,7 +114,6 @@ class ValidatorGroup : public td::actor::Actor { std::vector prev_block_ids_; BlockIdExt min_masterchain_block_id_; - UnixTime min_ts_; td::Ref validator_set_; BlockSeqno last_key_block_seqno_; @@ -139,10 +138,12 @@ class ValidatorGroup : public td::actor::Actor { std::vector> promises; }; std::shared_ptr cached_collated_block_; + td::CancellationTokenSource cancellation_token_source_; - void generated_block_candidate(std::shared_ptr cache, td::Result R); + void generated_block_candidate(validatorsession::BlockSourceInfo source_info, + std::shared_ptr cache, td::Result R); - typedef std::tuple CacheKey; + using CacheKey = std::tuple; std::map approved_candidates_cache_; void update_approve_cache(CacheKey key, UnixTime value); @@ -161,6 +162,16 @@ class ValidatorGroup : public td::actor::Actor { void add_available_block_candidate(td::Bits256 source, BlockIdExt id, FileHash collated_data_hash) { available_block_candidates_.emplace(source, id, collated_data_hash); } + + std::set sent_candidate_broadcasts_; + + void send_block_candidate_broadcast(BlockIdExt id, td::BufferSlice data) { + if (sent_candidate_broadcasts_.insert(id).second) { + td::actor::send_closure(manager_, &ValidatorManager::send_block_candidate_broadcast, id, + validator_set_->get_catchain_seqno(), validator_set_->get_validator_set_hash(), + std::move(data)); + } + } }; } // namespace validator diff --git a/validator/validator-options.hpp b/validator/validator-options.hpp index 900a682fd..203aa5ebc 100644 --- a/validator/validator-options.hpp +++ b/validator/validator-options.hpp @@ -138,6 +138,9 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { bool get_celldb_preload_all() const override { return celldb_preload_all_; } + bool get_celldb_in_memory() const override { + return celldb_in_memory_; + } td::optional get_catchain_max_block_delay() const override { return catchain_max_block_delay_; } @@ -230,6 +233,9 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { void set_celldb_preload_all(bool value) override { celldb_preload_all_ = value; } + void set_celldb_in_memory(bool value) override { + celldb_in_memory_ = value; + } void set_catchain_max_block_delay(double value) override { catchain_max_block_delay_ = value; } @@ -295,6 +301,7 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { td::optional celldb_cache_size_; bool celldb_direct_io_ = false; bool celldb_preload_all_ = false; + bool celldb_in_memory_ = false; td::optional catchain_max_block_delay_, catchain_max_block_delay_slow_; bool state_serializer_enabled_ = true; td::Ref collator_options_{true}; diff --git a/validator/validator.h b/validator/validator.h index afc32f3b9..9dbaa185f 100644 --- a/validator/validator.h +++ b/validator/validator.h @@ -102,6 +102,7 @@ struct ValidatorManagerOptions : public td::CntObject { virtual BlockSeqno sync_upto() const = 0; virtual std::string get_session_logs_file() const = 0; virtual td::uint32 get_celldb_compress_depth() const = 0; + virtual bool get_celldb_in_memory() const = 0; virtual size_t get_max_open_archive_files() const = 0; virtual double get_archive_preload_period() const = 0; virtual bool get_disable_rocksdb_stats() const = 0; @@ -141,6 +142,7 @@ struct ValidatorManagerOptions : public td::CntObject { virtual void set_celldb_cache_size(td::uint64 value) = 0; virtual void set_celldb_direct_io(bool value) = 0; virtual void set_celldb_preload_all(bool value) = 0; + virtual void set_celldb_in_memory(bool value) = 0; virtual void set_catchain_max_block_delay(double value) = 0; virtual void set_catchain_max_block_delay_slow(double value) = 0; virtual void set_state_serializer_enabled(bool value) = 0; @@ -172,7 +174,7 @@ class ValidatorManagerInterface : public td::actor::Actor { virtual void send_shard_block_info(BlockIdExt block_id, CatchainSeqno cc_seqno, td::BufferSlice data) = 0; virtual void send_block_candidate(BlockIdExt block_id, CatchainSeqno cc_seqno, td::uint32 validator_set_hash, td::BufferSlice data) = 0; - virtual void send_broadcast(BlockBroadcast broadcast, bool custom_overlays_only = false) = 0; + virtual void send_broadcast(BlockBroadcast broadcast, int mode) = 0; virtual void download_block(BlockIdExt block_id, td::uint32 priority, td::Timestamp timeout, td::Promise promise) = 0; virtual void download_zero_state(BlockIdExt block_id, td::uint32 priority, td::Timestamp timeout, @@ -254,6 +256,7 @@ class ValidatorManagerInterface : public td::actor::Actor { virtual void get_block_data_from_db_short(BlockIdExt block_id, td::Promise> promise) = 0; virtual void get_block_candidate_from_db(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash, td::Promise promise) = 0; + virtual void get_candidate_data_by_block_id_from_db(BlockIdExt id, td::Promise promise) = 0; virtual void get_shard_state_from_db(ConstBlockHandle handle, td::Promise> promise) = 0; virtual void get_shard_state_from_db_short(BlockIdExt block_id, td::Promise> promise) = 0; virtual void get_block_proof_from_db(ConstBlockHandle handle, td::Promise> promise) = 0; @@ -274,6 +277,7 @@ class ValidatorManagerInterface : public td::actor::Actor { virtual void run_ext_query(td::BufferSlice data, td::Promise promise) = 0; virtual void prepare_stats(td::Promise>> promise) = 0; + virtual void prepare_actor_stats(td::Promise promise) = 0; virtual void prepare_perf_timer_stats(td::Promise> promise) = 0; virtual void add_perf_timer_stat(std::string name, double duration) = 0;