diff --git a/.clang-tidy b/.clang-tidy index 2b16f55798..d4a9b1d3de 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -160,8 +160,6 @@ CheckOptions: value: '4294967295' - key: hicpp-member-init.IgnoreArrays value: '0' - - key: hicpp-move-const-arg.CheckTriviallyCopyableMove - value: '1' - key: hicpp-multiway-paths-covered.WarnOnMissingElse value: '0' - key: hicpp-named-parameter.IgnoreFailedSplit @@ -261,7 +259,7 @@ CheckOptions: - key: performance-inefficient-vector-operation.VectorLikeClasses value: '::std::vector' - key: performance-move-const-arg.CheckTriviallyCopyableMove - value: '1' + value: '0' - key: performance-move-constructor-init.IncludeStyle value: google - key: performance-type-promotion-in-math-fn.IncludeStyle diff --git a/.gitignore b/.gitignore index fabb982e03..2465a334f7 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ CMakeLists.txt.user .scannerwork/ peers.list cmake-build* +venv .gtm /.gtm/ @@ -33,4 +34,10 @@ examples/**/db /examples/kusama/syncing/ /examples/rococo/syncing/ -CMakeUserPresets.json \ No newline at end of file +CMakeUserPresets.json + +# Nix setup specific +.rustup +.cargo +.direnv +.envrc diff --git a/README.md b/README.md index 8e1177d890..6a02969e90 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,8 @@ More details of KAGOME development can be found within the [supported features]( #### Prerequisites -If you are using a Debian Linux system, the following command allows you to build KAGOME: +##### Debian/Ubuntu +If you are using a Debian-based Linux system, the following command allows you to build KAGOME: ```sh git clone https://github.com/qdrvm/kagome @@ -121,6 +122,14 @@ make debug_docker make clear ``` +##### Nix(OS) +With the Nix package manager or on NixOS, the project can be instantiated using +1. `nix develop`, or +2. `echo "use flake ." > .envrc && direnv allow` + +The build steps are otherwise the same as for Debian-based distros. +Note that with this setup, `.cargo` and `.rustup` will be local to the repository to avoid pollution of the host system. + ### Using KAGOME #### Obtaining database snapshot (optional) diff --git a/cmake/Hunter/config.cmake b/cmake/Hunter/config.cmake index a4bb35f002..fb292900dc 100644 --- a/cmake/Hunter/config.cmake +++ b/cmake/Hunter/config.cmake @@ -43,6 +43,12 @@ hunter_config( VERSION 1.85.0 ) +hunter_config( + BoringSSL + URL https://github.com/qdrvm/boringssl/archive/bc72a3fa91d52d6c1db45c24efd863d180c7d98c.zip + SHA1 64b281459365194c7d1f561b075c25138e122926 +) + if ("${WASM_COMPILER}" STREQUAL "WasmEdge") hunter_config( fmt @@ -73,6 +79,16 @@ if ("${WASM_COMPILER}" STREQUAL "WasmEdge") ) endif () +hunter_config( + WasmEdge + URL https://github.com/qdrvm/WasmEdge/archive/refs/heads/fix/libdir.zip + SHA1 0bd0d0d5dc3e377c65a17f2fe309c5cdb9668755 + CMAKE_ARGS + WASMEDGE_BUILD_STATIC_LIB=ON + WASMEDGE_BUILD_SHARED_LIB=OFF + KEEP_PACKAGE_SOURCES +) + if ("${WASM_COMPILER}" STREQUAL "WAVM") hunter_config( LLVM @@ -124,9 +140,8 @@ hunter_config( hunter_config( erasure_coding_crust -# VERSION 0.0.8 - URL https://github.com/qdrvm/erasure-coding-crust/archive/refs/tags/v0.0.8.tar.gz - SHA1 6bcdb6327f5da2dcec5c70f2fa63b95a44925af0 + URL https://github.com/qdrvm/erasure-coding-crust/archive/refs/heads/master.zip + SHA1 5b5970cce37c33f55929a0585445b05f27956dda KEEP_PACKAGE_SOURCES ) @@ -137,4 +152,3 @@ hunter_config( SHA1 1de495d8a3a73c1e940be3fdddf263a2d673aec1 KEEP_PACKAGE_SOURCES ) - diff --git a/core/application/chain_spec.hpp b/core/application/chain_spec.hpp index 7536f0d922..73c24bb0e7 100644 --- a/core/application/chain_spec.hpp +++ b/core/application/chain_spec.hpp @@ -8,7 +8,7 @@ #include -#include +#include #include "crypto/ed25519_types.hpp" #include "crypto/sr25519_types.hpp" @@ -38,8 +38,8 @@ namespace kagome::application { virtual const std::vector &bootNodes() const = 0; - virtual const std::vector> & - telemetryEndpoints() const = 0; + virtual const std::vector> + &telemetryEndpoints() const = 0; virtual const std::string &protocolId() const = 0; diff --git a/core/application/impl/chain_spec_impl.cpp b/core/application/impl/chain_spec_impl.cpp index 7b9fe52609..4760a9fce7 100644 --- a/core/application/impl/chain_spec_impl.cpp +++ b/core/application/impl/chain_spec_impl.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include diff --git a/core/authority_discovery/query/query_impl.cpp b/core/authority_discovery/query/query_impl.cpp index dd0e208f8e..b6ee4d77c6 100644 --- a/core/authority_discovery/query/query_impl.cpp +++ b/core/authority_discovery/query/query_impl.cpp @@ -10,6 +10,7 @@ #include "common/buffer_view.hpp" #include "common/bytestr.hpp" #include "crypto/sha/sha256.hpp" +#include "network/impl/protocols/parachain.hpp" #include "utils/retain_if.hpp" OUTCOME_CPP_DEFINE_CATEGORY(kagome::authority_discovery, QueryImpl::Error, e) { @@ -38,6 +39,7 @@ namespace kagome::authority_discovery { std::shared_ptr app_state_manager, std::shared_ptr block_tree, std::shared_ptr authority_discovery_api, + LazySPtr validation_protocol, std::shared_ptr key_store, std::shared_ptr sr_crypto_provider, std::shared_ptr libp2p_crypto_provider, @@ -47,6 +49,7 @@ namespace kagome::authority_discovery { std::shared_ptr scheduler) : block_tree_{std::move(block_tree)}, authority_discovery_api_{std::move(authority_discovery_api)}, + validation_protocol_{std::move(validation_protocol)}, key_store_{std::move(key_store)}, sr_crypto_provider_{std::move(sr_crypto_provider)}, libp2p_crypto_provider_{std::move(libp2p_crypto_provider)}, @@ -158,6 +161,7 @@ namespace kagome::authority_discovery { ++it; } else { it = auth_to_peer_cache_.erase(it); + validation_protocol_.get()->reserve(it->second.peer.id, false); } } for (auto it = peer_to_auth_cache_.begin(); @@ -166,6 +170,7 @@ namespace kagome::authority_discovery { ++it; } else { it = peer_to_auth_cache_.erase(it); + validation_protocol_.get()->reserve(it->first, false); } } std::shuffle(authorities.begin(), authorities.end(), random_); @@ -198,7 +203,8 @@ namespace kagome::authority_discovery { hash = common::Buffer{crypto::sha256(authority)}, authority] { if (auto self = wp.lock()) { - SL_DEBUG(self->log_, "start lookup({})", common::hex_lower(authority)); + SL_DEBUG( + self->log_, "start lookup({})", common::hex_lower(authority)); std::ignore = self->kademlia_.get()->getValue( hash, [=](const outcome::result> &res) { if (auto self = wp.lock()) { @@ -216,9 +222,9 @@ namespace kagome::authority_discovery { const primitives::AuthorityDiscoveryId &authority, outcome::result> _res) { SL_TRACE(log_, - "lookup : add addresses for authority {}, _res {}", - common::hex_lower(authority), - _res.has_value() ? "ok" : "error: " + _res.error().message()); + "lookup : add addresses for authority {}, _res {}", + common::hex_lower(authority), + _res.has_value() ? "ok" : "error: " + _res.error().message()); OUTCOME_TRY(signed_record_pb, _res); auto it = auth_to_peer_cache_.find(authority); if (it != auth_to_peer_cache_.end() @@ -271,9 +277,9 @@ namespace kagome::authority_discovery { libp2p::peer::PeerInfo peer{.id = std::move(peer_id)}; auto peer_id_str = peer.id.toBase58(); SL_TRACE(log_, - "lookup: adding {} addresses for authority {}", - record.addresses().size(), - authority); + "lookup: adding {} addresses for authority {}", + record.addresses().size(), + authority); for (auto &pb : record.addresses()) { OUTCOME_TRY(address, libp2p::multi::Multiaddress::create(str2byte(pb))); auto id = address.getPeerId(); @@ -316,9 +322,11 @@ namespace kagome::authority_discovery { Authority{ .raw = std::move(signed_record_pb), .time = time, - .peer = std::move(peer), + .peer = peer, }); + validation_protocol_.get()->reserve(peer.id, true); + return outcome::success(); } } // namespace kagome::authority_discovery diff --git a/core/authority_discovery/query/query_impl.hpp b/core/authority_discovery/query/query_impl.hpp index a4c6c55506..6a135ebbdb 100644 --- a/core/authority_discovery/query/query_impl.hpp +++ b/core/authority_discovery/query/query_impl.hpp @@ -26,6 +26,10 @@ #include #include +namespace kagome::network { + class ValidationProtocol; +} // namespace kagome::network + namespace kagome::authority_discovery { class QueryImpl : public Query, public libp2p::protocol::kademlia::Validator, @@ -43,6 +47,7 @@ namespace kagome::authority_discovery { std::shared_ptr app_state_manager, std::shared_ptr block_tree, std::shared_ptr authority_discovery_api, + LazySPtr validation_protocol, std::shared_ptr key_store, std::shared_ptr sr_crypto_provider, std::shared_ptr libp2p_crypto_provider, @@ -85,6 +90,7 @@ namespace kagome::authority_discovery { std::shared_ptr block_tree_; std::shared_ptr authority_discovery_api_; + LazySPtr validation_protocol_; std::shared_ptr key_store_; std::shared_ptr sr_crypto_provider_; std::shared_ptr libp2p_crypto_provider_; diff --git a/core/blockchain/genesis_block_hash.hpp b/core/blockchain/genesis_block_hash.hpp index bb096f8c54..ec18835456 100644 --- a/core/blockchain/genesis_block_hash.hpp +++ b/core/blockchain/genesis_block_hash.hpp @@ -6,7 +6,7 @@ #pragma once -#include "blockchain/block_tree.hpp" +#include "primitives/common.hpp" namespace kagome::blockchain { diff --git a/core/consensus/babe/impl/babe.cpp b/core/consensus/babe/impl/babe.cpp index 93f5413532..a7de4a47ee 100644 --- a/core/consensus/babe/impl/babe.cpp +++ b/core/consensus/babe/impl/babe.cpp @@ -35,7 +35,7 @@ #include "offchain/offchain_worker_pool.hpp" #include "parachain/availability/bitfield/store.hpp" #include "parachain/parachain_inherent_data.hpp" -#include "parachain/validator/parachain_processor.hpp" +#include "parachain/validator/backed_candidates_source.hpp" #include "primitives/inherent_data.hpp" #include "runtime/runtime_api/babe_api.hpp" #include "runtime/runtime_api/offchain_worker_api.hpp" diff --git a/core/consensus/babe/impl/babe.hpp b/core/consensus/babe/impl/babe.hpp index 9d49cb04c2..2a050d5de3 100644 --- a/core/consensus/babe/impl/babe.hpp +++ b/core/consensus/babe/impl/babe.hpp @@ -71,7 +71,7 @@ namespace kagome::offchain { namespace kagome::parachain { class BitfieldStore; - struct ParachainProcessorImpl; + class ParachainProcessorImpl; struct BackedCandidatesSource; } // namespace kagome::parachain diff --git a/core/consensus/babe/impl/babe_block_validator_impl.cpp b/core/consensus/babe/impl/babe_block_validator_impl.cpp index 68398bb89e..d297a5ac1f 100644 --- a/core/consensus/babe/impl/babe_block_validator_impl.cpp +++ b/core/consensus/babe/impl/babe_block_validator_impl.cpp @@ -24,6 +24,7 @@ #include "runtime/runtime_api/babe_api.hpp" #include "runtime/runtime_api/offchain_worker_api.hpp" #include "threshold_util.hpp" +#include "utils/weak_macro.hpp" OUTCOME_CPP_DEFINE_CATEGORY(kagome::consensus::babe, BabeBlockValidatorImpl::ValidationError, @@ -77,22 +78,9 @@ namespace kagome::consensus::babe { void BabeBlockValidatorImpl::prepare() { sync_state_observer_ = - std::make_shared( - sync_state_observable_, false); - sync_state_observer_->subscribe( - sync_state_observer_->generateSubscriptionSetId(), - primitives::events::SyncStateEventType::kSyncState); - sync_state_observer_->setCallback( - [wp{weak_from_this()}]( - auto /*set_id*/, - bool &synchronized, - auto /*event_type*/, - const primitives::events::SyncStateEventParams &event) mutable { - if (auto self = wp.lock()) { - if (event == consensus::SyncState::SYNCHRONIZED) { - self->was_synchronized_ = true; - } - } + primitives::events::onSync(sync_state_observable_, [WEAK_SELF] { + WEAK_LOCK(self); + self->was_synchronized_ = true; }); } diff --git a/core/consensus/babe/impl/babe_block_validator_impl.hpp b/core/consensus/babe/impl/babe_block_validator_impl.hpp index 2218e2bdec..39614ecb62 100644 --- a/core/consensus/babe/impl/babe_block_validator_impl.hpp +++ b/core/consensus/babe/impl/babe_block_validator_impl.hpp @@ -126,7 +126,7 @@ namespace kagome::consensus::babe { primitives::events::SyncStateSubscriptionEnginePtr sync_state_observable_; bool was_synchronized_ = false; - primitives::events::SyncStateEventSubscriberPtr sync_state_observer_; + std::shared_ptr sync_state_observer_; }; } // namespace kagome::consensus::babe diff --git a/core/consensus/beefy/impl/beefy_impl.cpp b/core/consensus/beefy/impl/beefy_impl.cpp index 7a7aaf7fdb..b242198f72 100644 --- a/core/consensus/beefy/impl/beefy_impl.cpp +++ b/core/consensus/beefy/impl/beefy_impl.cpp @@ -38,18 +38,14 @@ namespace kagome::network { constexpr std::chrono::minutes kRebroadcastAfter{1}; - namespace { - // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) - metrics::GaugeHelper metric_validator_set_id{ - "kagome_beefy_validator_set_id", - "Current BEEFY active validator set id.", - }; - // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) - metrics::GaugeHelper metric_finalized{ - "kagome_beefy_best_block", - "Best block finalized by BEEFY", - }; - } // namespace + static const metrics::GaugeHelper metric_validator_set_id{ + "kagome_beefy_validator_set_id", + "Current BEEFY active validator set id.", + }; + static const metrics::GaugeHelper metric_finalized{ + "kagome_beefy_best_block", + "Best block finalized by BEEFY", + }; BeefyImpl::BeefyImpl( std::shared_ptr app_state_manager, diff --git a/core/dispute_coordinator/impl/dispute_coordinator_impl.cpp b/core/dispute_coordinator/impl/dispute_coordinator_impl.cpp index 5fd93564ce..9ce629399d 100644 --- a/core/dispute_coordinator/impl/dispute_coordinator_impl.cpp +++ b/core/dispute_coordinator/impl/dispute_coordinator_impl.cpp @@ -15,6 +15,7 @@ #include #include "application/app_state_manager.hpp" +#include "application/chain_spec.hpp" #include "authority_discovery/query/query.hpp" #include "blockchain/block_header_repository.hpp" #include "common/main_thread_pool.hpp" @@ -35,6 +36,7 @@ #include "runtime/runtime_api/parachain_host.hpp" #include "utils/pool_handler_ready_make.hpp" #include "utils/tuple_hash.hpp" +#include "utils/weak_macro.hpp" namespace kagome::dispute { @@ -238,15 +240,12 @@ namespace kagome::dispute { active_heads_.insert(leaves.begin(), leaves.end()); // subscribe to leaves update - my_view_sub_ = std::make_shared( - peer_view_->getMyViewObservable(), false); - primitives::events::subscribe( - *my_view_sub_, + my_view_sub_ = primitives::events::subscribe( + peer_view_->getMyViewObservable(), network::PeerView::EventType::kViewUpdated, - [wptr{weak_from_this()}](const network::ExView &event) { - if (auto self = wptr.lock()) { - self->on_active_leaves_update(event); - } + [WEAK_SELF](const network::ExView &event) { + WEAK_LOCK(self); + self->on_active_leaves_update(event); }); // subscribe to finalization diff --git a/core/dispute_coordinator/impl/dispute_coordinator_impl.hpp b/core/dispute_coordinator/impl/dispute_coordinator_impl.hpp index 212db01b32..744a37b3c8 100644 --- a/core/dispute_coordinator/impl/dispute_coordinator_impl.hpp +++ b/core/dispute_coordinator/impl/dispute_coordinator_impl.hpp @@ -72,7 +72,7 @@ namespace kagome::network { } // namespace kagome::network namespace kagome::parachain { - struct ApprovalDistribution; + class ApprovalDistribution; class Recovery; class Pvf; } // namespace kagome::parachain diff --git a/core/injector/application_injector.cpp b/core/injector/application_injector.cpp index 6e063f3f57..75f479a8eb 100644 --- a/core/injector/application_injector.cpp +++ b/core/injector/application_injector.cpp @@ -4,12 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "injector/application_injector.hpp" - #define BOOST_DI_CFG_DIAGNOSTICS_LEVEL 2 #define BOOST_DI_CFG_CTOR_LIMIT_SIZE \ 32 // TODO(Harrm): #2104 check how it influences on compilation time +#include "injector/application_injector.hpp" + #include #include #include @@ -119,10 +119,11 @@ #include "network/impl/peer_manager_impl.hpp" #include "network/impl/protocols/beefy_justification_protocol.hpp" #include "network/impl/protocols/beefy_protocol_impl.hpp" +#include "network/impl/protocols/block_announce_protocol.hpp" #include "network/impl/protocols/fetch_attested_candidate.hpp" #include "network/impl/protocols/grandpa_protocol.hpp" #include "network/impl/protocols/light.hpp" -#include "network/impl/protocols/parachain_protocols.hpp" +#include "network/impl/protocols/parachain.hpp" #include "network/impl/protocols/protocol_fetch_available_data.hpp" #include "network/impl/protocols/protocol_fetch_chunk.hpp" #include "network/impl/protocols/protocol_fetch_chunk_obsolete.hpp" @@ -328,7 +329,6 @@ namespace { libp2p::protocol::kademlia::Config kademlia_config; kademlia_config.protocols = network::make_protocols("/{}/kad", genesis, chain_spec); - kademlia_config.maxBucketSize = 1000; kademlia_config.randomWalk.enabled = false; kademlia_config.valueLookupsQuorum = 4; @@ -757,7 +757,6 @@ namespace { di::bind.template to(), di::bind.template to(), di::bind.template to(), - di::bind.template to(), di::bind.template to(), di::bind.template to(), di::bind.template to(), @@ -935,7 +934,7 @@ namespace kagome::injector { KagomeNodeInjector::KagomeNodeInjector( sptr app_config) : pimpl_{std::make_unique( - makeKagomeNodeInjector(std::move(app_config)))} {} + makeKagomeNodeInjector(std::move(app_config)))} {} sptr KagomeNodeInjector::injectAppConfig() { return pimpl_->injector_ diff --git a/core/injector/application_injector.hpp b/core/injector/application_injector.hpp index 89ae6faaf6..e891b8a64c 100644 --- a/core/injector/application_injector.hpp +++ b/core/injector/application_injector.hpp @@ -58,11 +58,11 @@ namespace kagome { namespace parachain { class ParachainObserver; - struct ParachainProcessorImpl; - struct ApprovalDistribution; + class ParachainProcessorImpl; + class ApprovalDistribution; namespace statement_distribution { - struct StatementDistribution; + class StatementDistribution; } } // namespace parachain diff --git a/core/metrics/histogram_timer.hpp b/core/metrics/histogram_timer.hpp index 2e1c386f4b..28fbc17654 100644 --- a/core/metrics/histogram_timer.hpp +++ b/core/metrics/histogram_timer.hpp @@ -27,7 +27,7 @@ namespace kagome::metrics { metric_ = registry_->registerGaugeMetric(name); } - auto *operator->() { + auto *operator->() const { return metric_; } diff --git a/core/network/CMakeLists.txt b/core/network/CMakeLists.txt index 8166b8b0d2..c04661a4c0 100644 --- a/core/network/CMakeLists.txt +++ b/core/network/CMakeLists.txt @@ -9,6 +9,7 @@ add_subdirectory(protobuf) add_library(network impl/protocols/light.cpp + impl/protocols/parachain.cpp impl/state_protocol_observer_impl.cpp impl/state_sync_request_flow.cpp impl/synchronizer_impl.cpp @@ -18,7 +19,6 @@ add_library(network impl/extrinsic_observer_impl.cpp impl/transactions_transmitter_impl.cpp impl/sync_protocol_observer_impl.cpp - impl/stream_engine.cpp impl/protocols/protocol_error.cpp impl/protocols/state_protocol_impl.cpp impl/protocols/sync_protocol_impl.cpp @@ -32,6 +32,7 @@ add_library(network impl/peer_manager_impl.cpp impl/reputation_repository_impl.cpp helpers/scale_message_read_writer.cpp + notifications/protocol.cpp adapters/adapter_errors.cpp impl/protocols/protocol_req_pov.cpp warp/cache.cpp diff --git a/core/network/collation_observer.hpp b/core/network/collation_observer.hpp index f6aa145bb3..cd529b5e35 100644 --- a/core/network/collation_observer.hpp +++ b/core/network/collation_observer.hpp @@ -19,11 +19,6 @@ namespace kagome::network { public: virtual ~CollationObserver() = default; - /// Handle incoming collation stream. - virtual void onIncomingCollationStream( - const libp2p::peer::PeerId &peer_id, - network::CollationVersion version) = 0; - /// Handle incoming collation message. virtual void onIncomingMessage( const libp2p::peer::PeerId &peer_id, diff --git a/core/network/helpers/new_stream.hpp b/core/network/helpers/new_stream.hpp new file mode 100644 index 0000000000..3f79d0694a --- /dev/null +++ b/core/network/helpers/new_stream.hpp @@ -0,0 +1,26 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +namespace kagome::network { + /** + * Use existing connection or dial using known addresses. + */ + inline void newStream(libp2p::Host &host, + const libp2p::PeerId &peer, + libp2p::StreamProtocols protocols, + libp2p::StreamAndProtocolOrErrorCb cb) { + libp2p::peer::PeerInfo info{.id = peer, .addresses = {}}; + if (auto r = host.getPeerRepository().getAddressRepository().getAddresses( + peer)) { + info.addresses = r.value(); + } + host.newStream(info, std::move(protocols), std::move(cb)); + } +} // namespace kagome::network diff --git a/core/network/impl/peer_manager_impl.cpp b/core/network/impl/peer_manager_impl.cpp index b7b5e9b6bd..1e6224013e 100644 --- a/core/network/impl/peer_manager_impl.cpp +++ b/core/network/impl/peer_manager_impl.cpp @@ -15,18 +15,12 @@ #include "common/main_thread_pool.hpp" #include "network/can_disconnect.hpp" -#include "network/impl/protocols/beefy_protocol_impl.hpp" -#include "network/impl/protocols/grandpa_protocol.hpp" -#include "network/impl/protocols/parachain_protocols.hpp" -#include "network/protocols/beefy_protocol.hpp" -#include "outcome/outcome.hpp" #include "scale/libp2p_types.hpp" #include "storage/predefined_keys.hpp" #include "utils/pool_handler_ready_make.hpp" +#include "utils/weak_macro.hpp" namespace { - constexpr const char *syncPeerMetricName = "kagome_sync_peers"; - constexpr const char *kPeersCountMetricName = "kagome_sub_libp2p_peers_count"; /// Reputation value for a node when we get disconnected from it. static constexpr int32_t kDisconnectReputation = -256; /// Reputation change for a node when we get disconnected from it. @@ -43,38 +37,6 @@ OUTCOME_CPP_DEFINE_CATEGORY(kagome::network, PeerManagerImpl::Error, e) { return "Unknown error in ChainSpecImpl"; } -namespace { - - template - bool openOutgoing( - std::shared_ptr &stream_engine, - const std::shared_ptr

&protocol, - const kagome::network::PeerManager::PeerInfo &peer_info, - F &&func) { // NOLINT(cppcoreguidelines-missing-std-forward) - BOOST_ASSERT(stream_engine); - BOOST_ASSERT(protocol); - - if (stream_engine->reserveOutgoing(peer_info.id, protocol)) { - protocol->newOutgoingStream( - peer_info.id, - [peer_id{peer_info.id}, - wptr_proto{std::weak_ptr

{protocol}}, - wptr_se{std::weak_ptr{stream_engine}}, - func{std::forward(func)}](auto &&stream) mutable { - auto stream_engine = wptr_se.lock(); - auto proto = wptr_proto.lock(); - if (stream_engine && proto) { - stream_engine->dropReserveOutgoing(peer_id, proto); - } - std::forward(func)(std::forward(stream)); - }); - return true; - } - return false; - } - -} // namespace - namespace kagome::network { PeerManagerImpl::PeerManagerImpl( std::shared_ptr app_state_manager, @@ -83,7 +45,6 @@ namespace kagome::network { std::shared_ptr identify, std::shared_ptr kademlia, std::shared_ptr scheduler, - std::shared_ptr stream_engine, const application::AppConfiguration &app_config, std::shared_ptr clock, const BootstrapNodes &bootstrap_nodes, @@ -102,7 +63,6 @@ namespace kagome::network { identify_(std::move(identify)), kademlia_(std::move(kademlia)), scheduler_(std::move(scheduler)), - stream_engine_(std::move(stream_engine)), app_config_(app_config), clock_(std::move(clock)), bootstrap_nodes_(bootstrap_nodes), @@ -117,7 +77,6 @@ namespace kagome::network { BOOST_ASSERT(identify_ != nullptr); BOOST_ASSERT(kademlia_ != nullptr); BOOST_ASSERT(scheduler_ != nullptr); - BOOST_ASSERT(stream_engine_ != nullptr); BOOST_ASSERT(router_ != nullptr); BOOST_ASSERT(storage_ != nullptr); BOOST_ASSERT(hasher_ != nullptr); @@ -126,16 +85,6 @@ namespace kagome::network { BOOST_ASSERT(peer_view_ != nullptr); BOOST_ASSERT(peer_event_engine_); - // Register metrics - registry_->registerGaugeFamily(syncPeerMetricName, - "Number of peers we sync with"); - registry_->registerGaugeFamily(kPeersCountMetricName, - "Number of connected peers"); - sync_peer_num_ = registry_->registerGaugeMetric(syncPeerMetricName); - sync_peer_num_->set(0); - peers_count_metric_ = registry_->registerGaugeMetric(kPeersCountMetricName); - peers_count_metric_->set(0); - app_state_manager->takeControl(*this); } @@ -176,6 +125,29 @@ namespace kagome::network { } }); + peer_connected_sub_ = + host_.getBus() + .getChannel() + .subscribe( + [WEAK_SELF]( + const std::weak_ptr + &weak_conn) { + WEAK_LOCK(self); + WEAK_LOCK(conn); + auto peer_id = conn->remotePeer().value(); + for (auto &conn2 : self->host_.getNetwork() + .getConnectionManager() + .getConnectionsToPeer(peer_id)) { + if (conn2 == conn) { + continue; + } + if (conn2->isInitiator() != conn->isInitiator()) { + continue; + } + std::ignore = conn->close(); + } + }); + peer_disconnected_handler_ = host_.getBus() .getChannel() @@ -184,14 +156,10 @@ namespace kagome::network { SL_DEBUG(self->log_, "OnPeerDisconnectedChannel handler from peer {}", peer_id); - self->stream_engine_->del(peer_id); self->peer_states_.erase(peer_id); self->active_peers_.erase(peer_id); self->connecting_peers_.erase(peer_id); self->peer_view_->removePeer(peer_id); - self->sync_peer_num_->set(self->active_peers_.size()); - self->peers_count_metric_->set(self->active_peers_.size()); - self->peer_event_engine_->notify( primitives::events::PeerEventType::kDisconnected, peer_id); SL_DEBUG(self->log_, @@ -262,11 +230,6 @@ namespace kagome::network { return active_peers_.size(); } - std::shared_ptr PeerManagerImpl::getStreamEngine() { - BOOST_ASSERT(stream_engine_); - return stream_engine_; - } - void PeerManagerImpl::forEachPeer( std::function func) const { for (auto &it : active_peers_) { @@ -287,10 +250,6 @@ namespace kagome::network { }; it->second.time = clock_->now(); } - - auto proto_col = router_->getCollationProtocolVStaging(); - BOOST_ASSERT_MSG(proto_col, "Router did not provide collaction protocol"); - stream_engine_->reserveStreams(peer_id, proto_col); } void PeerManagerImpl::forOnePeer( @@ -587,7 +546,6 @@ namespace kagome::network { auto &state = peer_states_[peer_id]; state.time = clock_->now(); state.best_block = announce.header.blockInfo(); - state.known_blocks.add(state.best_block.hash); } void PeerManagerImpl::updatePeerState( @@ -654,153 +612,6 @@ namespace kagome::network { queue_to_connect_.size()); } - template - void PeerManagerImpl::openBlockAnnounceProtocol( - const PeerInfo &peer_info, - const libp2p::network::ConnectionManager::ConnectionSPtr &connection, - F &&opened_callback) { - auto protocol = router_->getBlockAnnounceProtocol(); - BOOST_ASSERT_MSG(protocol, - "Router did not provide block announce protocol"); - - if (!openOutgoing( - stream_engine_, - protocol, - peer_info, - [wp{weak_from_this()}, - peer_info, - protocol, - connection, - opened_callback = - std::forward(opened_callback)](auto &&stream_res) mutable { - auto self = wp.lock(); - if (not self) { - return; - } - - auto &peer_id = peer_info.id; - - if (not stream_res.has_value()) { - SL_VERBOSE(self->log_, - "Unable to create stream {} with {}: {}", - protocol->protocolName(), - peer_id, - stream_res.error()); - self->connecting_peers_.erase(peer_id); - self->disconnectFromPeer(peer_id); - return; - } - PeerType peer_type = connection->isInitiator() - ? PeerType::PEER_TYPE_OUT - : PeerType::PEER_TYPE_IN; - - // Add to the active peer list - if (auto [ap_it, added] = self->active_peers_.emplace( - peer_id, - PeerDescriptor{.peer_type = peer_type, - .time_point = self->clock_->now()}); - added) { - self->recently_active_peers_.insert(peer_id); - - // And remove from queue - if (auto piq_it = self->peers_in_queue_.find(peer_id); - piq_it != self->peers_in_queue_.end()) { - auto qtc_it = std::ranges::find_if( - self->queue_to_connect_.cbegin(), - self->queue_to_connect_.cend(), - [&peer_id = peer_id](const auto &item) { - return peer_id == item; - }); - self->queue_to_connect_.erase(qtc_it); - self->peers_in_queue_.erase(piq_it); - BOOST_ASSERT(self->queue_to_connect_.size() - == self->peers_in_queue_.size()); - - SL_DEBUG(self->log_, - "Remained peers in queue for connect: {}", - self->peers_in_queue_.size()); - } - self->sync_peer_num_->set(self->active_peers_.size()); - self->peers_count_metric_->set(self->active_peers_.size()); - } - - self->connecting_peers_.erase(peer_id); - - self->reserveStreams(peer_id); - self->reserveStatusStreams(peer_id); - self->startPingingPeer(peer_id); - - /// Process callback when opened successfully - std::forward(opened_callback)( - self, peer_info, self->getPeerState(peer_id)); - })) { - SL_DEBUG(log_, - "Stream {} with {} is alive or connecting", - protocol->protocolName(), - peer_info.id); - } - } - - void PeerManagerImpl::tryOpenGrandpaProtocol(const PeerInfo &peer_info, - PeerState &r_info) { - if (auto o_info_opt = getPeerState(own_peer_info_.id); - o_info_opt.has_value()) { - auto &o_info = o_info_opt.value(); - - // Establish outgoing grandpa stream if node synced - if (r_info.best_block.number <= o_info.get().best_block.number) { - auto grandpa_protocol = router_->getGrandpaProtocol(); - BOOST_ASSERT_MSG(grandpa_protocol, - "Router did not provide grandpa protocol"); - openOutgoing( - stream_engine_, grandpa_protocol, peer_info, [](const auto &...) { - }); - } - } - } - - void PeerManagerImpl::tryOpenValidationProtocol( - const PeerInfo &peer_info, - PeerState &peer_state, - network::CollationVersion - proto_version) { // network::CollationVersion::VStaging - /// If validator start validation protocol - if (peer_state.roles.isAuthority()) { - auto validation_protocol = [&]() -> std::shared_ptr { - return router_->getValidationProtocolVStaging(); - }(); - - BOOST_ASSERT_MSG(validation_protocol, - "Router did not provide validation protocol"); - - log_->trace("Try to open outgoing validation protocol.(peer={})", - peer_info.id); - openOutgoing(stream_engine_, - validation_protocol, - peer_info, - [validation_protocol, peer_info, wptr{weak_from_this()}]( - outcome::result> stream_result) { - auto self = wptr.lock(); - if (not self) { - return; - } - - auto &peer_id = peer_info.id; - if (!stream_result.has_value()) { - SL_TRACE(self->log_, - "Unable to create stream {} with {}: {}", - validation_protocol->protocolName(), - peer_id, - stream_result.error()); - return; - } - - self->stream_engine_->addOutgoing(stream_result.value(), - validation_protocol); - }); - } - } - void PeerManagerImpl::processFullyConnectedPeer(const PeerId &peer_id) { // Skip connection to itself if (isSelfPeer(peer_id)) { @@ -815,14 +626,6 @@ namespace kagome::network { connecting_peers_.erase(peer_id); return; } - auto out = connection->isInitiator(); - if (out) { - if (countPeers(PeerType::PEER_TYPE_OUT) >= app_config_.outPeers()) { - connecting_peers_.erase(peer_id); - disconnectFromPeer(peer_id); - return; - } - } // Don't accept connection from bad (negative reputation) peers const auto peer_reputation = reputation_repository_->reputation(peer_id); @@ -838,45 +641,9 @@ namespace kagome::network { } PeerInfo peer_info{.id = peer_id, .addresses = {}}; - openBlockAnnounceProtocol( - peer_info, - connection, - [out](std::shared_ptr &self, - const PeerInfo &peer_info, - std::optional> peer_state) { - if (peer_state.has_value()) { - auto &state = peer_state->get(); - if (not out) { - if (state.roles.isFull()) { - if (self->countPeers(PeerType::PEER_TYPE_IN) - >= self->app_config_.inPeers()) { - self->connecting_peers_.erase(peer_info.id); - self->disconnectFromPeer(peer_info.id); - return; - } - } else { - if (self->countPeers(PeerType::PEER_TYPE_IN, IsLight(true)) - >= self->app_config_.inPeersLight()) { - self->connecting_peers_.erase(peer_info.id); - self->disconnectFromPeer(peer_info.id); - return; - } - } - } - self->peer_event_engine_->notify( - primitives::events::PeerEventType::kConnected, peer_info.id); - self->tryOpenGrandpaProtocol(peer_info, peer_state.value().get()); - auto beefy_protocol = std::static_pointer_cast( - self->router_->getBeefyProtocol()); - openOutgoing( - self->stream_engine_, - beefy_protocol, - peer_info, - [](const outcome::result< - std::shared_ptr> &) {}); - } - }); + peer_event_engine_->notify(primitives::events::PeerEventType::kConnected, + peer_info.id); auto addresses_res = host_.getPeerRepository().getAddressRepository().getAddresses(peer_id); @@ -887,30 +654,6 @@ namespace kagome::network { } } - void PeerManagerImpl::reserveStatusStreams(const PeerId &peer_id) const { - if (auto ps = getPeerState(peer_id); ps && ps->get().roles.isAuthority()) { - auto proto_val_vstaging = router_->getValidationProtocolVStaging(); - BOOST_ASSERT_MSG(proto_val_vstaging, - "Router did not provide validation protocol vstaging"); - - stream_engine_->reserveStreams(peer_id, proto_val_vstaging); - } - } - - void PeerManagerImpl::reserveStreams(const PeerId &peer_id) const { - // Reserve stream slots for needed protocols - auto grandpa_protocol = router_->getGrandpaProtocol(); - BOOST_ASSERT_MSG(grandpa_protocol, - "Router did not provide grandpa protocol"); - - auto transaction_protocol = router_->getPropagateTransactionsProtocol(); - BOOST_ASSERT_MSG(transaction_protocol, - "Router did not provide propagate transaction protocol"); - - stream_engine_->reserveStreams(peer_id, grandpa_protocol); - stream_engine_->reserveStreams(peer_id, transaction_protocol); - } - bool PeerManagerImpl::isSelfPeer(const PeerId &peer_id) const { return own_peer_info_.id == peer_id; } diff --git a/core/network/impl/peer_manager_impl.hpp b/core/network/impl/peer_manager_impl.hpp index c690677809..a8e1f803b3 100644 --- a/core/network/impl/peer_manager_impl.hpp +++ b/core/network/impl/peer_manager_impl.hpp @@ -27,10 +27,7 @@ #include "crypto/hasher.hpp" #include "injector/lazy.hpp" #include "log/logger.hpp" -#include "metrics/metrics.hpp" -#include "network/impl/protocols/block_announce_protocol.hpp" #include "network/impl/protocols/propagate_transactions_protocol.hpp" -#include "network/impl/stream_engine.hpp" #include "network/peer_view.hpp" #include "network/protocols/sync_protocol.hpp" #include "network/reputation_repository.hpp" @@ -69,7 +66,6 @@ namespace kagome::network { std::shared_ptr identify, std::shared_ptr kademlia, std::shared_ptr scheduler, - std::shared_ptr stream_engine, const application::AppConfiguration &app_config, std::shared_ptr clock, const BootstrapNodes &bootstrap_nodes, @@ -91,15 +87,6 @@ namespace kagome::network { /** @see PeerManager::connectToPeer */ void connectToPeer(const PeerInfo &peer_info) override; - /** @see PeerManager::reserveStreams */ - void reserveStreams(const PeerId &peer_id) const override; - - /** @see PeerManager::reserveStatusStreams */ - void reserveStatusStreams(const PeerId &peer_id) const override; - - /** @see PeerManager::getStreamEngine */ - std::shared_ptr getStreamEngine() override; - /** @see PeerManager::activePeersNumber */ size_t activePeersNumber() const override; @@ -164,17 +151,6 @@ namespace kagome::network { void processFullyConnectedPeer(const PeerId &peer_id); - template - void openBlockAnnounceProtocol( - const PeerInfo &peer_info, - const libp2p::network::ConnectionManager::ConnectionSPtr &connection, - F &&opened_callback); - void tryOpenGrandpaProtocol(const PeerInfo &peer_info, - PeerState &peer_state); - void tryOpenValidationProtocol(const PeerInfo &peer_info, - PeerState &peer_state, - network::CollationVersion proto_version); - /// Opens streams set for special peer (i.e. new-discovered) void connectToPeer(const PeerId &peer_id); @@ -199,7 +175,6 @@ namespace kagome::network { std::shared_ptr identify_; std::shared_ptr kademlia_; std::shared_ptr scheduler_; - std::shared_ptr stream_engine_; const application::AppConfiguration &app_config_; std::shared_ptr clock_; const BootstrapNodes &bootstrap_nodes_; @@ -212,6 +187,7 @@ namespace kagome::network { std::shared_ptr peer_view_; libp2p::event::Handle add_peer_handle_; + libp2p::event::Handle peer_connected_sub_; libp2p::event::Handle peer_disconnected_handler_; std::unordered_set peers_in_queue_; std::deque queue_to_connect_; @@ -224,11 +200,6 @@ namespace kagome::network { libp2p::basic::Scheduler::Handle align_timer_; std::set recently_active_peers_; primitives::events::PeerSubscriptionEnginePtr peer_event_engine_; - - // metrics - metrics::RegistryPtr registry_ = metrics::createRegistry(); - metrics::Gauge *sync_peer_num_; - metrics::Gauge *peers_count_metric_; }; } // namespace kagome::network diff --git a/core/network/impl/peer_view.cpp b/core/network/impl/peer_view.cpp index a3b06baaca..0854e3df72 100644 --- a/core/network/impl/peer_view.cpp +++ b/core/network/impl/peer_view.cpp @@ -7,19 +7,29 @@ #include "network/peer_view.hpp" #include "blockchain/block_tree.hpp" #include "common/visitor.hpp" +#include "utils/weak_macro.hpp" namespace kagome::network { + inline View makeView(const LazySPtr &block_tree) { + View view{ + .heads_ = block_tree.get()->getLeaves(), + .finalized_number_ = block_tree.get()->getLastFinalized().number, + }; + std::ranges::sort(view.heads_); + return view; + } PeerView::PeerView( primitives::events::ChainSubscriptionEnginePtr chain_events_engine, std::shared_ptr app_state_manager, LazySPtr block_tree) : chain_sub_{std::move(chain_events_engine)}, + block_tree_{std::move(block_tree)}, my_view_update_observable_{ std::make_shared()}, remote_view_update_observable_{ std::make_shared()}, - block_tree_(block_tree) { + my_view_{makeView(block_tree_)} { app_state_manager->takeControl(*this); } @@ -28,21 +38,10 @@ namespace kagome::network { } bool PeerView::prepare() { - chain_sub_.onHead( - [weak{weak_from_this()}](const primitives::BlockHeader &header) { - if (auto self = weak.lock()) { - self->updateMyView(ExView{ - .view = - View{ - .heads_ = self->block_tree_.get()->getLeaves(), - .finalized_number_ = - self->block_tree_.get()->getLastFinalized().number, - }, - .new_head = header, - .lost = {}, - }); - } - }); + chain_sub_.onHead([WEAK_SELF](const primitives::BlockHeader &header) { + WEAK_LOCK(self); + self->updateMyView(header); + }); return true; } @@ -56,25 +55,23 @@ namespace kagome::network { return remote_view_update_observable_; } - void PeerView::updateMyView(network::ExView &&view) { + void PeerView::updateMyView(const primitives::BlockHeader &header) { BOOST_ASSERT(my_view_update_observable_); - std::ranges::sort(view.view.heads_); - if (!my_view_ || my_view_->view != view.view - || my_view_->new_head != view.new_head) { - if (my_view_) { - view.lost.swap(my_view_->lost); - view.lost.clear(); - for (const auto &head : my_view_->view.heads_) { - if (!view.view.contains(head)) { - view.lost.emplace_back(head); - } - } + ExView event{ + .view = makeView(block_tree_), + .new_head = header, + .lost = {}, + }; + if (event.view == my_view_) { + return; + } + for (const auto &head : my_view_.heads_) { + if (not event.view.contains(head)) { + event.lost.emplace_back(head); } - my_view_ = std::move(view); - - BOOST_ASSERT(my_view_); - my_view_update_observable_->notify(EventType::kViewUpdated, *my_view_); } + my_view_ = event.view; + my_view_update_observable_->notify(EventType::kViewUpdated, event); } size_t PeerView::peersCount() const { @@ -115,10 +112,4 @@ namespace kagome::network { EventType::kViewUpdated, peer_id, *ref); } } - - std::optional> PeerView::getMyView() - const { - return my_view_; - } - } // namespace kagome::network diff --git a/core/network/impl/protocols/beefy_justification_protocol.hpp b/core/network/impl/protocols/beefy_justification_protocol.hpp index 3291e3b0a1..e75b9d54ba 100644 --- a/core/network/impl/protocols/beefy_justification_protocol.hpp +++ b/core/network/impl/protocols/beefy_justification_protocol.hpp @@ -27,7 +27,6 @@ namespace kagome::common { namespace kagome::network { class Beefy; class PeerManager; - struct StreamEngine; } // namespace kagome::network namespace kagome::network { diff --git a/core/network/impl/protocols/beefy_protocol_impl.cpp b/core/network/impl/protocols/beefy_protocol_impl.cpp index 8e156baf26..5e8d8846b2 100644 --- a/core/network/impl/protocols/beefy_protocol_impl.cpp +++ b/core/network/impl/protocols/beefy_protocol_impl.cpp @@ -8,79 +8,57 @@ #include "consensus/beefy/beefy.hpp" #include "network/common.hpp" -#include "network/impl/stream_engine.hpp" -#include "network/notifications/connect_and_handshake.hpp" -#include "network/notifications/handshake_and_read_messages.hpp" +#include "network/notifications/encode.hpp" +#include "utils/try.hpp" namespace kagome::network { + using consensus::beefy::BeefyGossipMessage; - BeefyProtocolImpl::BeefyProtocolImpl(libp2p::Host &host, - const blockchain::GenesisBlockHash &genesis, - Roles roles, - std::shared_ptr beefy, - std::shared_ptrstream_engine - ) - : base_{ - kName, - host, - make_protocols(kBeefyProtocol, genesis), - log::createLogger(kName), - }, + // https://github.com/paritytech/polkadot-sdk/blob/edf79aa972bcf2e043e18065a9bb860ecdbd1a6e/substrate/client/consensus/beefy/src/communication/mod.rs#L82-L83 + constexpr size_t kPeersLimit = 25; + + BeefyProtocolImpl::BeefyProtocolImpl( + const notifications::Factory ¬ifications_factory, + const blockchain::GenesisBlockHash &genesis, + Roles roles, + std::shared_ptr beefy) + : notifications_{notifications_factory.make( + {make_protocols(kBeefyProtocol, genesis)}, kPeersLimit, kPeersLimit)}, roles_{roles}, - beefy_{std::move(beefy)}, - stream_engine_{std::move(stream_engine)} - {} + beefy_{std::move(beefy)} {} - bool BeefyProtocolImpl::start() { - return base_.start(weak_from_this()); + Buffer BeefyProtocolImpl::handshake() { + return scale::encode(roles_).value(); } - const std::string &BeefyProtocolImpl::protocolName() const { - return base_.protocolName(); + bool BeefyProtocolImpl::onHandshake(const PeerId &peer_id, + size_t, + bool, + Buffer &&handshake) { + TRY_FALSE(scale::decode(handshake)); + return true; } - void BeefyProtocolImpl::onIncomingStream(std::shared_ptr stream) { - auto on_handshake = [](std::shared_ptr self, - std::shared_ptr stream, - Roles) { - self->stream_engine_->addIncoming(stream, self); - return true; - }; - auto on_message = [](std::shared_ptr self, - consensus::beefy::BeefyGossipMessage message) { - self->beefy_->onMessage(std::move(message)); - return true; - }; - notifications::handshakeAndReadMessages< - consensus::beefy::BeefyGossipMessage>(weak_from_this(), - std::move(stream), - roles_, - std::move(on_handshake), - std::move(on_message)); + bool BeefyProtocolImpl::onMessage(const PeerId &peer_id, + size_t, + Buffer &&message_raw) { + auto message = TRY_FALSE(scale::decode(message_raw)); + beefy_->onMessage(std::move(message)); + return true; } - void BeefyProtocolImpl::newOutgoingStream( - const PeerId &peer_id, - std::function>)> &&cb) { - auto on_handshake = - [cb = std::move(cb)]( - std::shared_ptr self, - outcome::result> - r) mutable { - if (not r) { - cb(r.error()); - return; - } - auto &stream = std::get<0>(r.value()); - self->stream_engine_->addOutgoing(stream, self); - cb(std::move(stream)); - }; - notifications::connectAndHandshake( - weak_from_this(), base_, peer_id, roles_, std::move(on_handshake)); - } + void BeefyProtocolImpl::onClose(const PeerId &peer_id) {} void BeefyProtocolImpl::broadcast( std::shared_ptr message) { - stream_engine_->broadcast(shared_from_this(), message); + auto message_raw = notifications::encode(message); + notifications_->peersOut([&](const PeerId &peer_id, size_t) { + notifications_->write(peer_id, message_raw); + return true; + }); + } + + void BeefyProtocolImpl::start() { + return notifications_->start(weak_from_this()); } } // namespace kagome::network diff --git a/core/network/impl/protocols/beefy_protocol_impl.hpp b/core/network/impl/protocols/beefy_protocol_impl.hpp index 968d799b6c..8470531f00 100644 --- a/core/network/impl/protocols/beefy_protocol_impl.hpp +++ b/core/network/impl/protocols/beefy_protocol_impl.hpp @@ -6,51 +6,51 @@ #pragma once -#include "consensus/beefy/types.hpp" -#include "network/helpers/scale_message_read_writer.hpp" -#include "network/impl/protocols/request_response_protocol.hpp" +#include "network/notifications/protocol.hpp" #include "network/protocols/beefy_protocol.hpp" #include "network/types/roles.hpp" namespace kagome::blockchain { class GenesisBlockHash; -} +} // namespace kagome::blockchain namespace kagome::network { class Beefy; - struct StreamEngine; } // namespace kagome::network namespace kagome::network { + using libp2p::PeerId; class BeefyProtocolImpl final - : public BeefyProtocol, - public std::enable_shared_from_this { + : public std::enable_shared_from_this, + public notifications::Controller, + public BeefyProtocol { static constexpr auto kName = "BeefyProtocol"; public: - BeefyProtocolImpl(libp2p::Host &host, + BeefyProtocolImpl(const notifications::Factory ¬ifications_factory, const blockchain::GenesisBlockHash &genesis, Roles roles, - std::shared_ptr beefy, - std::shared_ptr stream_engine); - - bool start() override; - const std::string &protocolName() const override; - void onIncomingStream(std::shared_ptr stream) override; - void newOutgoingStream( - const PeerId &peer_id, - std::function>)> &&cb) - override; - + std::shared_ptr beefy); + + // Controller + Buffer handshake() override; + bool onHandshake(const PeerId &peer_id, + size_t, + bool, + Buffer &&handshake) override; + bool onMessage(const PeerId &peer_id, size_t, Buffer &&message) override; + void onClose(const PeerId &peer_id) override; + + // BeefyProtocol void broadcast( std::shared_ptr message) override; + void start(); + private: - ProtocolBaseImpl base_; + std::shared_ptr notifications_; Roles roles_; std::shared_ptr beefy_; - std::shared_ptr stream_engine_; }; - } // namespace kagome::network diff --git a/core/network/impl/protocols/block_announce_protocol.cpp b/core/network/impl/protocols/block_announce_protocol.cpp index f34b4c0da1..cfce8d191a 100644 --- a/core/network/impl/protocols/block_announce_protocol.cpp +++ b/core/network/impl/protocols/block_announce_protocol.cpp @@ -6,153 +6,150 @@ #include "network/impl/protocols/block_announce_protocol.hpp" +#include "application/app_configuration.hpp" +#include "application/chain_spec.hpp" +#include "blockchain/block_tree.hpp" #include "blockchain/genesis_block_hash.hpp" +#include "common/main_thread_pool.hpp" +#include "crypto/hasher.hpp" +#include "metrics/histogram_timer.hpp" +#include "network/block_announce_observer.hpp" #include "network/common.hpp" -#include "network/helpers/scale_message_read_writer.hpp" -#include "network/impl/protocols/protocol_error.hpp" -#include "network/notifications/connect_and_handshake.hpp" -#include "network/notifications/handshake_and_read_messages.hpp" +#include "network/impl/protocols/grandpa_protocol.hpp" +#include "network/impl/protocols/propagate_transactions_protocol.hpp" +#include "network/notifications/encode.hpp" +#include "network/peer_manager.hpp" +#include "utils/try.hpp" +#include "utils/weak_macro.hpp" namespace kagome::network { - - // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) - KAGOME_DEFINE_CACHE(BlockAnnounceProtocol); + // https://github.com/paritytech/polkadot-sdk/blob/edf79aa972bcf2e043e18065a9bb860ecdbd1a6e/substrate/client/network/sync/src/engine.rs#L86 + constexpr size_t kSeenCapacity = 1024; + + static const struct { + void inc(bool inc) const { + for (auto &metric : metrics) { + if (inc) { + metric->inc(); + } else { + metric->dec(); + } + } + } + std::array metrics{ + metrics::GaugeHelper{ + "kagome_sync_peers", + "Number of peers we sync with", + }, + metrics::GaugeHelper{ + "kagome_sub_libp2p_peers_count", + "Number of connected peers", + }, + }; + } metric_peers; BlockAnnounceProtocol::BlockAnnounceProtocol( - libp2p::Host &host, + MainThreadPool &main_thread_pool, + const application::AppConfiguration &app_config, + const notifications::Factory ¬ifications_factory, Roles roles, const application::ChainSpec &chain_spec, const blockchain::GenesisBlockHash &genesis_hash, - std::shared_ptr stream_engine, std::shared_ptr block_tree, std::shared_ptr observer, + LazySPtr grandpa_protocol, + LazySPtr transaction_protocol, std::shared_ptr hasher, + telemetry::PeerCount telemetry_peer_count, std::shared_ptr peer_manager) - : base_(kBlockAnnounceProtocolName, - host, - make_protocols(kBlockAnnouncesProtocol, genesis_hash, chain_spec), - log::createLogger(kBlockAnnounceProtocolName, - "block_announce_protocol")), - roles_{roles}, - stream_engine_(std::move(stream_engine)), + : main_pool_handler_{main_thread_pool.handlerStarted()}, + notifications_{notifications_factory.make( + {make_protocols(kBlockAnnouncesProtocol, genesis_hash, chain_spec)}, + app_config.inPeers(), + app_config.outPeers())}, + handshake_{.roles = roles, .genesis_hash = genesis_hash}, block_tree_(std::move(block_tree)), observer_(std::move(observer)), + grandpa_protocol_(std::move(grandpa_protocol)), + transaction_protocol_(std::move(transaction_protocol)), hasher_(std::move(hasher)), - peer_manager_(std::move(peer_manager)) { - BOOST_ASSERT(stream_engine_ != nullptr); + telemetry_peer_count_(std::move(telemetry_peer_count)), + peer_manager_{std::move(peer_manager)}, + seen_{kSeenCapacity} { BOOST_ASSERT(block_tree_ != nullptr); BOOST_ASSERT(observer_ != nullptr); BOOST_ASSERT(peer_manager_ != nullptr); } - bool BlockAnnounceProtocol::start() { - return base_.start(weak_from_this()); + Buffer BlockAnnounceProtocol::handshake() { + auto handshake = handshake_; + handshake.best_block = block_tree_->bestBlock(); + return scale::encode(handshake).value(); } - const ProtocolName &BlockAnnounceProtocol::protocolName() const { - return base_.protocolName(); - } - - BlockAnnounceHandshake BlockAnnounceProtocol::createHandshake() const { - return BlockAnnounceHandshake{ - .roles = roles_, - .best_block = block_tree_->bestBlock(), - .genesis_hash = block_tree_->getGenesisBlockHash(), - }; - } - - bool BlockAnnounceProtocol::onHandshake( - const PeerId &peer, const BlockAnnounceHandshake &handshake) const { + bool BlockAnnounceProtocol::onHandshake(const PeerId &peer_id, + size_t, + bool, + Buffer &&handshake_raw) { + auto handshake = + TRY_FALSE(scale::decode(handshake_raw)); if (handshake.genesis_hash != block_tree_->getGenesisBlockHash()) { return false; } - peer_manager_->updatePeerState(peer, handshake); - observer_->onBlockAnnounceHandshake(peer, handshake); + if (seen_.add(peer_id)) { + metric_peers.inc(true); + ++*telemetry_peer_count_.v; + } + grandpa_protocol_.get()->notifications_->reserve(peer_id, true); + transaction_protocol_.get()->notifications_->reserve(peer_id, true); + main_pool_handler_->execute( + [WEAK_SELF, peer_id, handshake{std::move(handshake)}] { + WEAK_LOCK(self); + self->peer_manager_->updatePeerState(peer_id, handshake); + self->observer_->onBlockAnnounceHandshake(peer_id, handshake); + self->peer_manager_->startPingingPeer(peer_id); + }); return true; } - void BlockAnnounceProtocol::onIncomingStream(std::shared_ptr stream) { - BOOST_ASSERT(stream->remotePeerId().has_value()); - auto peer_id = stream->remotePeerId().value(); - auto on_handshake = [peer_id](std::shared_ptr self, - std::shared_ptr stream, - BlockAnnounceHandshake handshake) { - if (not self->onHandshake(peer_id, handshake)) { - return false; - } - self->stream_engine_->addIncoming(stream, self); - self->peer_manager_->reserveStreams(peer_id); - self->peer_manager_->reserveStatusStreams(peer_id); - self->peer_manager_->startPingingPeer(peer_id); - return true; - }; - auto on_message = [peer_id](std::shared_ptr self, - const BlockAnnounce &block_announce) { - // Calculate and save hash, 'cause it's just received announce - primitives::calculateBlockHash(block_announce.header, *self->hasher_); - - SL_VERBOSE(self->base_.logger(), - "Announce of block {} is received from {}", - block_announce.header.blockInfo(), - peer_id); - - self->peer_manager_->updatePeerState(peer_id, block_announce); - self->observer_->onBlockAnnounce(peer_id, block_announce); + bool BlockAnnounceProtocol::onMessage(const PeerId &peer_id, + size_t, + Buffer &&message_raw) { + auto block_announce = TRY_FALSE(scale::decode(message_raw)); + primitives::calculateBlockHash(block_announce.header, *hasher_); + if (not seen_.add(peer_id, block_announce.header.hash())) { return true; - }; - notifications::handshakeAndReadMessages( - weak_from_this(), - std::move(stream), - createHandshake(), - std::move(on_handshake), - std::move(on_message)); + } + main_pool_handler_->execute( + [WEAK_SELF, peer_id, block_announce{std::move(block_announce)}] { + WEAK_LOCK(self); + self->peer_manager_->updatePeerState(peer_id, block_announce); + self->observer_->onBlockAnnounce(peer_id, block_announce); + }); + return true; } - void BlockAnnounceProtocol::newOutgoingStream( - const PeerId &peer_id, - std::function>)> &&cb) { - SL_DEBUG(base_.logger(), - "Connect for {} stream with {}", - protocolName(), - peer_id); + void BlockAnnounceProtocol::onClose(const PeerId &peer_id) { + metric_peers.inc(false); + --*telemetry_peer_count_.v; + seen_.remove(peer_id); + grandpa_protocol_.get()->notifications_->reserve(peer_id, false); + transaction_protocol_.get()->notifications_->reserve(peer_id, false); + } - auto on_handshake = [peer_id, cb = std::move(cb)]( - std::shared_ptr self, - outcome::result> r) mutable { - if (not r) { - cb(r.error()); - return; - } - if (not self->onHandshake(peer_id, std::get<2>(r.value()))) { - cb(ProtocolError::GENESIS_NO_MATCH); - return; - } - auto &stream = std::get<0>(r.value()); - self->stream_engine_->addOutgoing(stream, self); - cb(std::move(stream)); - }; - notifications::connectAndHandshake(weak_from_this(), - base_, - peer_id, - createHandshake(), - std::move(on_handshake)); + void BlockAnnounceProtocol::start() { + notifications_->start(weak_from_this()); } void BlockAnnounceProtocol::blockAnnounce(BlockAnnounce &&announce) { - auto shared_msg = - KAGOME_EXTRACT_SHARED_CACHE(BlockAnnounceProtocol, BlockAnnounce); - (*shared_msg) = std::move(announce); - const auto &hash = shared_msg->header.hash(); - - SL_DEBUG( - base_.logger(), "Send announce of block #{}", announce.header.number); - - stream_engine_->broadcast( - shared_from_this(), shared_msg, [&](const PeerId &peer) { - auto state = peer_manager_->getPeerState(peer); - return state and state->get().known_blocks.add(hash); - }); + REINVOKE(*main_pool_handler_, blockAnnounce, std::move(announce)); + auto message_raw = notifications::encode(announce); + notifications_->peersOut([&](const PeerId &peer_id, size_t) { + if (not seen_.add(peer_id, announce.header.hash())) { + return true; + } + notifications_->write(peer_id, message_raw); + return true; + }); } - } // namespace kagome::network diff --git a/core/network/impl/protocols/block_announce_protocol.hpp b/core/network/impl/protocols/block_announce_protocol.hpp index 1e621ae58f..07566fc7f9 100644 --- a/core/network/impl/protocols/block_announce_protocol.hpp +++ b/core/network/impl/protocols/block_announce_protocol.hpp @@ -6,79 +6,84 @@ #pragma once -#include "network/protocol_base.hpp" - -#include - -#include -#include - -#include "application/chain_spec.hpp" -#include "blockchain/block_tree.hpp" -#include "containers/objects_cache.hpp" -#include "crypto/hasher.hpp" -#include "log/logger.hpp" -#include "network/block_announce_observer.hpp" -#include "network/impl/protocols/protocol_base_impl.hpp" -#include "network/impl/stream_engine.hpp" -#include "network/peer_manager.hpp" +#include "injector/lazy.hpp" +#include "network/notifications/protocol.hpp" #include "network/types/block_announce.hpp" #include "network/types/block_announce_handshake.hpp" +#include "telemetry/peer_count.hpp" +#include "utils/lru.hpp" #include "utils/non_copyable.hpp" +namespace kagome::application { + class AppConfiguration; + class ChainSpec; +} // namespace kagome::application + namespace kagome::blockchain { class GenesisBlockHash; -} + class BlockTree; +} // namespace kagome::blockchain + +namespace kagome::crypto { + class Hasher; +} // namespace kagome::crypto namespace kagome::network { + struct BlockAnnounceObserver; + class GrandpaProtocol; + class PeerManager; + class PropagateTransactionsProtocol; +} // namespace kagome::network - KAGOME_DECLARE_CACHE(BlockAnnounceProtocol, KAGOME_CACHE_UNIT(BlockAnnounce)); +namespace kagome::network { + using common::MainThreadPool; + using libp2p::PeerId; class BlockAnnounceProtocol final - : public ProtocolBase, - public std::enable_shared_from_this, + : public std::enable_shared_from_this, + public notifications::Controller, NonCopyable, NonMovable { public: - BlockAnnounceProtocol() = delete; - ~BlockAnnounceProtocol() override = default; - - BlockAnnounceProtocol(libp2p::Host &host, - Roles roles, - const application::ChainSpec &chain_spec, - const blockchain::GenesisBlockHash &genesis_hash, - std::shared_ptr stream_engine, - std::shared_ptr block_tree, - std::shared_ptr observer, - std::shared_ptr hasher, - std::shared_ptr peer_manager); - - bool start() override; - - const std::string &protocolName() const override; - - void onIncomingStream(std::shared_ptr stream) override; - void newOutgoingStream( - const PeerId &peer_id, - std::function>)> &&cb) - override; - + BlockAnnounceProtocol( + MainThreadPool &main_thread_pool, + const application::AppConfiguration &app_config, + const notifications::Factory ¬ifications_factory, + Roles roles, + const application::ChainSpec &chain_spec, + const blockchain::GenesisBlockHash &genesis_hash, + std::shared_ptr block_tree, + std::shared_ptr observer, + LazySPtr grandpa_protocol, + LazySPtr transaction_protocol, + std::shared_ptr hasher, + telemetry::PeerCount telemetry_peer_count, + std::shared_ptr peer_manager); + + // Controller + Buffer handshake() override; + bool onHandshake(const PeerId &peer_id, + size_t, + bool, + Buffer &&handshake) override; + bool onMessage(const PeerId &peer_id, size_t, Buffer &&message) override; + void onClose(const PeerId &peer_id) override; + + void start(); void blockAnnounce(BlockAnnounce &&announce); private: - BlockAnnounceHandshake createHandshake() const; - bool onHandshake(const PeerId &peer, - const BlockAnnounceHandshake &handshake) const; - - inline static const auto kBlockAnnounceProtocolName = - "BlockAnnounceProtocol"s; - ProtocolBaseImpl base_; - Roles roles_; - std::shared_ptr stream_engine_; + std::shared_ptr main_pool_handler_; + std::shared_ptr notifications_; + BlockAnnounceHandshake handshake_; std::shared_ptr block_tree_; std::shared_ptr observer_; + LazySPtr grandpa_protocol_; + LazySPtr transaction_protocol_; std::shared_ptr hasher_; + telemetry::PeerCount telemetry_peer_count_; std::shared_ptr peer_manager_; + MapLruSet seen_; }; } // namespace kagome::network diff --git a/core/network/impl/protocols/fetch_attested_candidate.hpp b/core/network/impl/protocols/fetch_attested_candidate.hpp index 1f4a72f2f7..d4f2bd95e7 100644 --- a/core/network/impl/protocols/fetch_attested_candidate.hpp +++ b/core/network/impl/protocols/fetch_attested_candidate.hpp @@ -16,9 +16,8 @@ #include "blockchain/genesis_block_hash.hpp" #include "log/logger.hpp" #include "network/common.hpp" +#include "network/helpers/scale_message_read_writer.hpp" #include "network/impl/protocols/request_response_protocol.hpp" -#include "network/impl/stream_engine.hpp" -#include "parachain/validator/parachain_processor.hpp" #include "parachain/validator/statement_distribution/statement_distribution.hpp" #include "utils/non_copyable.hpp" @@ -39,17 +38,17 @@ namespace kagome::network { parachain::statement_distribution::StatementDistribution> statement_distribution) : RequestResponseProtocolImpl< - vstaging::AttestedCandidateRequest, - vstaging::AttestedCandidateResponse, - ScaleMessageReadWriter>{kFetchAttestedCandidateProtocolName, - host, - make_protocols( - kFetchAttestedCandidateProtocol, - genesis_hash, - kProtocolPrefixPolkadot), - log::createLogger( - kFetchAttestedCandidateProtocolName, - "req_attested_candidate_protocol")}, + vstaging::AttestedCandidateRequest, + vstaging::AttestedCandidateResponse, + ScaleMessageReadWriter>{kFetchAttestedCandidateProtocolName, + host, + make_protocols( + kFetchAttestedCandidateProtocol, + genesis_hash, + kProtocolPrefixPolkadot), + log::createLogger( + kFetchAttestedCandidateProtocolName, + "req_attested_candidate_protocol")}, statement_distribution_(std::move(statement_distribution)) { BOOST_ASSERT(statement_distribution_); } diff --git a/core/network/impl/protocols/grandpa_protocol.cpp b/core/network/impl/protocols/grandpa_protocol.cpp index 81023551e0..7604e4b5ec 100644 --- a/core/network/impl/protocols/grandpa_protocol.cpp +++ b/core/network/impl/protocols/grandpa_protocol.cpp @@ -6,126 +6,83 @@ #include "network/impl/protocols/grandpa_protocol.hpp" -#include "blockchain/block_tree.hpp" +#include + #include "blockchain/genesis_block_hash.hpp" +#include "consensus/grandpa/grandpa_observer.hpp" #include "network/common.hpp" -#include "network/impl/protocols/protocol_error.hpp" -#include "network/notifications/connect_and_handshake.hpp" -#include "network/notifications/handshake_and_read_messages.hpp" +#include "network/notifications/encode.hpp" #include "network/peer_manager.hpp" -#include "network/types/grandpa_message.hpp" -#include "network/types/roles.hpp" +#include "network/types/own_peer_info.hpp" +#include "utils/try.hpp" namespace kagome::network { - - // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) - KAGOME_DEFINE_CACHE(GrandpaProtocol); + // https://github.com/paritytech/polkadot-sdk/blob/edf79aa972bcf2e043e18065a9bb860ecdbd1a6e/substrate/client/network-gossip/src/state_machine.rs#L40 + constexpr size_t kSeenCapacity = 8192; GrandpaProtocol::GrandpaProtocol( - libp2p::Host &host, + const notifications::Factory ¬ifications_factory, std::shared_ptr hasher, - std::shared_ptr io_context, Roles roles, std::shared_ptr grandpa_observer, const OwnPeerInfo &own_info, - std::shared_ptr stream_engine, std::shared_ptr peer_manager, const blockchain::GenesisBlockHash &genesis_hash, std::shared_ptr scheduler) - : base_(kGrandpaProtocolName, - host, - make_protocols( - kGrandpaProtocol, genesis_hash, kProtocolPrefixParitytech), - log::createLogger(kGrandpaProtocolName, "grandpa_protocol")), + : log_{log::createLogger(kGrandpaProtocolName, "grandpa_protocol")}, + notifications_{notifications_factory.make( + {make_protocols( + kGrandpaProtocol, genesis_hash, kProtocolPrefixParitytech)}, + 0, + 0)}, hasher_{std::move(hasher)}, - io_context_(std::move(io_context)), roles_{roles}, grandpa_observer_(std::move(grandpa_observer)), own_info_(own_info), - stream_engine_(std::move(stream_engine)), peer_manager_(std::move(peer_manager)), - scheduler_(std::move(scheduler)) {} + scheduler_{std::move(scheduler)}, + seen_{kSeenCapacity} {} - bool GrandpaProtocol::start() { - return base_.start(weak_from_this()); + Buffer GrandpaProtocol::handshake() { + return scale::encode(roles_).value(); } - const ProtocolName &GrandpaProtocol::protocolName() const { - return base_.protocolName(); - } - - void GrandpaProtocol::onIncomingStream(std::shared_ptr stream) { - BOOST_ASSERT(stream->remotePeerId().has_value()); - auto on_handshake = [](std::shared_ptr self, - std::shared_ptr stream, - Roles) { - self->stream_engine_->addIncoming(stream, self); - return true; - }; - auto on_message = [peer_id = stream->remotePeerId().value()]( - std::shared_ptr self, - GrandpaMessage message) { - self->onMessage(peer_id, std::move(message)); - return true; - }; - notifications::handshakeAndReadMessages( - weak_from_this(), - std::move(stream), - roles_, - std::move(on_handshake), - std::move(on_message)); - } - - void GrandpaProtocol::newOutgoingStream( - const PeerId &peer_id, - std::function>)> &&cb) { - auto on_handshake = - [cb = std::move(cb)]( - std::shared_ptr self, - outcome::result> - r) mutable { - if (not r) { - cb(r.error()); - return; - } - auto &stream = std::get<0>(r.value()); - self->stream_engine_->addOutgoing(stream, self); - auto shared_msg = - KAGOME_EXTRACT_SHARED_CACHE(GrandpaProtocol, GrandpaMessage); - (*shared_msg) = self->last_neighbor_; - self->stream_engine_->send( - stream->remotePeerId().value(), self, std::move(shared_msg)); - cb(std::move(stream)); - }; - notifications::connectAndHandshake( - weak_from_this(), base_, peer_id, roles_, std::move(on_handshake)); + bool GrandpaProtocol::onHandshake(const PeerId &peer_id, + size_t, + bool out, + Buffer &&handshake) { + TRY_FALSE(scale::decode(handshake)); + if (out) { + write(peer_id, rawMessage(last_neighbor_)); + } + return true; } - void GrandpaProtocol::onMessage(const PeerId &peer_id, - GrandpaMessage message) { - auto hash = getHash(message); + bool GrandpaProtocol::onMessage(const PeerId &peer_id, + size_t, + Buffer &&message_raw) { + auto message = TRY_FALSE(scale::decode(message_raw)); + if (auto hash = rawMessageHash(message, message_raw)) { + if (not seen_.add(peer_id, *hash)) { + return true; + } + } visit_in_place( std::move(message), [&](network::GrandpaVote &&vote_message) { - SL_VERBOSE( - base_.logger(), "VoteMessage has received from {}", peer_id); + SL_VERBOSE(log_, "VoteMessage has received from {}", peer_id); auto info = peer_manager_->getPeerState(peer_id); grandpa_observer_->onVoteMessage( peer_id, compactFromRefToOwn(info), std::move(vote_message)); - addKnown(peer_id, hash); }, [&](FullCommitMessage &&commit_message) { - SL_VERBOSE( - base_.logger(), "CommitMessage has received from {}", peer_id); + SL_VERBOSE(log_, "CommitMessage has received from {}", peer_id); grandpa_observer_->onCommitMessage(peer_id, std::move(commit_message)); - addKnown(peer_id, hash); }, [&](GrandpaNeighborMessage &&neighbor_message) { if (peer_id != own_info_.id) { - SL_VERBOSE(base_.logger(), - "NeighborMessage has received from {}", - peer_id); + SL_VERBOSE(log_, "NeighborMessage has received from {}", peer_id); auto info = peer_manager_->getPeerState(peer_id); grandpa_observer_->onNeighborMessage( peer_id, @@ -135,8 +92,7 @@ namespace kagome::network { } }, [&](network::CatchUpRequest &&catch_up_request) { - SL_VERBOSE( - base_.logger(), "CatchUpRequest has received from {}", peer_id); + SL_VERBOSE(log_, "CatchUpRequest has received from {}", peer_id); auto info = peer_manager_->getPeerState(peer_id); grandpa_observer_->onCatchUpRequest( peer_id, @@ -145,17 +101,25 @@ namespace kagome::network { std::move(catch_up_request)); }, [&](network::CatchUpResponse &&catch_up_response) { - SL_VERBOSE( - base_.logger(), "CatchUpResponse has received from {}", peer_id); + SL_VERBOSE(log_, "CatchUpResponse has received from {}", peer_id); grandpa_observer_->onCatchUpResponse(peer_id, std::move(catch_up_response)); }); + return true; + } + + void GrandpaProtocol::onClose(const PeerId &peer_id) { + seen_.remove(peer_id); + } + + void GrandpaProtocol::start() { + notifications_->start(weak_from_this()); } void GrandpaProtocol::vote( network::GrandpaVote &&vote_message, std::optional peer_id) { - SL_DEBUG(base_.logger(), + SL_DEBUG(log_, "Send vote message: grandpa round number {}", vote_message.round_number); @@ -166,7 +130,7 @@ namespace kagome::network { } if (not info.set_id.has_value() or not info.round_number.has_value()) { - SL_DEBUG(base_.logger(), + SL_DEBUG(log_, "Vote signed by {} with set_id={} in round={} " "has not been sent to {}: set id or round number unknown", msg.id(), @@ -180,7 +144,7 @@ namespace kagome::network { // from an earlier voter set. It is extremely impolite to send messages // from a future voter set. if (msg.counter != info.set_id) { - SL_DEBUG(base_.logger(), + SL_DEBUG(log_, "Vote signed by {} with set_id={} in round={} " "has not been sent to {} as impolite: their set id is {}", msg.id(), @@ -194,7 +158,7 @@ namespace kagome::network { // only r-1 ... r+1 if (msg.round_number + 1 < info.round_number.value()) { SL_DEBUG( - base_.logger(), + log_, "Vote signed by {} with set_id={} in round={} " "has not been sent to {} as impolite: their round is already {}", msg.id(), @@ -206,7 +170,7 @@ namespace kagome::network { } if (msg.round_number > info.round_number.value() + 1) { - SL_DEBUG(base_.logger(), + SL_DEBUG(log_, "Vote signed by {} with set_id={} in round={} " "has not been sent to {} as impolite: their round is old: {}", msg.id(), @@ -220,17 +184,12 @@ namespace kagome::network { return true; }; - auto shared_msg = - KAGOME_EXTRACT_SHARED_CACHE(GrandpaProtocol, GrandpaMessage); - (*shared_msg) = GrandpaMessage(std::move(vote_message)); - + auto raw_message = rawMessage(vote_message); if (not peer_id.has_value()) { - broadcast(std::move(shared_msg), filter); + broadcast(raw_message, filter); } else { - addKnown(*peer_id, getHash(*shared_msg)); - stream_engine_->send( - peer_id.value(), shared_from_this(), std::move(shared_msg)); - }; + write(*peer_id, std::move(raw_message)); + } } void GrandpaProtocol::neighbor(GrandpaNeighborMessage &&msg) { @@ -240,7 +199,7 @@ namespace kagome::network { auto set_changed = msg.voter_set_id != last_neighbor_.voter_set_id; last_neighbor_ = msg; - SL_DEBUG(base_.logger(), + SL_DEBUG(log_, "Send neighbor message: grandpa round number {}", msg.round_number); @@ -252,7 +211,7 @@ namespace kagome::network { round_number = msg.round_number](const PeerId &peer_id) { auto info_opt = peer_manager_->getPeerState(peer_id); if (not info_opt.has_value()) { - SL_DEBUG(base_.logger(), + SL_DEBUG(log_, "Neighbor message with set_id={} in round={} " "has not been sent to {}: peer is not connected", set_id, @@ -269,20 +228,19 @@ namespace kagome::network { return true; }; - auto shared_msg = - KAGOME_EXTRACT_SHARED_CACHE(GrandpaProtocol, GrandpaMessage); - (*shared_msg) = GrandpaMessage(msg); - - stream_engine_->broadcast( - shared_from_this(), shared_msg, filter); + auto raw_message = rawMessage(msg); + notifications_->peersOut([&](const PeerId &peer_id, size_t) { + if (filter(peer_id)) { + write(peer_id, raw_message); + } + return true; + }); } void GrandpaProtocol::finalize( FullCommitMessage &&msg, std::optional peer_id) { - SL_DEBUG(base_.logger(), - "Send commit message: grandpa round number {}", - msg.round); + SL_DEBUG(log_, "Send commit message: grandpa round number {}", msg.round); auto filter = [this, set_id = msg.set_id, @@ -290,7 +248,7 @@ namespace kagome::network { finalizing = msg.message.target_number]( const PeerId &peer_id, const PeerState &info) { if (not info.set_id.has_value() or not info.round_number.has_value()) { - SL_DEBUG(base_.logger(), + SL_DEBUG(log_, "Commit with set_id={} in round={} " "has not been sent to {}: set id or round number unknown", set_id, @@ -302,7 +260,7 @@ namespace kagome::network { // It is especially impolite to send commits which are invalid, or from // a different Set ID than the receiving peer has indicated. if (set_id != info.set_id) { - SL_DEBUG(base_.logger(), + SL_DEBUG(log_, "Commit with set_id={} in round={} " "has not been sent to {} as impolite: their set id is {}", set_id, @@ -315,7 +273,7 @@ namespace kagome::network { // Don't send commit if that has not actual for remote peer already if (round_number < info.round_number.value()) { SL_DEBUG( - base_.logger(), + log_, "Commit with set_id={} in round={} " "has not been sent to {} as impolite: their round is already {}", set_id, @@ -329,7 +287,7 @@ namespace kagome::network { // sent. if (finalizing < info.last_finalized) { SL_DEBUG( - base_.logger(), + log_, "Commit with set_id={} in round={} " "has not been sent to {} as impolite: their round is already {}", set_id, @@ -342,30 +300,25 @@ namespace kagome::network { return true; }; - auto shared_msg = - KAGOME_EXTRACT_SHARED_CACHE(GrandpaProtocol, GrandpaMessage); - (*shared_msg) = GrandpaMessage(std::move(msg)); - + auto raw_message = rawMessage(msg); if (not peer_id.has_value()) { - broadcast(std::move(shared_msg), filter); + broadcast(raw_message, filter); } else { - addKnown(*peer_id, getHash(*shared_msg)); - stream_engine_->send( - peer_id.value(), shared_from_this(), std::move(shared_msg)); + write(*peer_id, std::move(raw_message)); } } void GrandpaProtocol::catchUpRequest(const libp2p::peer::PeerId &peer_id, CatchUpRequest &&catch_up_request) { SL_DEBUG( - base_.logger(), + log_, "Send catch-up-request to {} beginning with grandpa round number {}", peer_id, catch_up_request.round_number); auto info_opt = peer_manager_->getPeerState(peer_id); if (not info_opt.has_value()) { - SL_DEBUG(base_.logger(), + SL_DEBUG(log_, "Catch-up-request with set_id={} in round={} " "has not been sent to {}: peer is not connected", catch_up_request.voter_set_id, @@ -376,7 +329,7 @@ namespace kagome::network { const auto &info = info_opt.value().get(); if (not info.set_id.has_value() or not info.round_number.has_value()) { - SL_DEBUG(base_.logger(), + SL_DEBUG(log_, "Catch-up-request with set_id={} in round={} " "has not been sent to {}: set id or round number unknown", catch_up_request.voter_set_id, @@ -387,7 +340,7 @@ namespace kagome::network { // Impolite to send a catch up request to a peer in a new different Set ID. if (catch_up_request.voter_set_id != info.set_id) { - SL_DEBUG(base_.logger(), + SL_DEBUG(log_, "Catch-up-request with set_id={} in round={} " "has not been sent to {}: different set id", catch_up_request.voter_set_id, @@ -399,7 +352,7 @@ namespace kagome::network { // It is impolite to send a catch-up request for a round `R` to a peer // whose announced view is behind `R`. if (catch_up_request.round_number < info.round_number.value() - 1) { - SL_DEBUG(base_.logger(), + SL_DEBUG(log_, "Catch-up-request with set_id={} in round={} " "has not been sent to {}: too old round for requested", catch_up_request.voter_set_id, @@ -414,7 +367,7 @@ namespace kagome::network { recent_catchup_requests_by_round_.emplace(round_id); if (not ok_by_round) { - SL_DEBUG(base_.logger(), + SL_DEBUG(log_, "Catch-up-request with set_id={} in round={} " "has not been sent to {}: " "the same catch-up request had sent to another peer", @@ -430,7 +383,7 @@ namespace kagome::network { // It is impolite to replay a catch-up request if (not ok_by_peer) { recent_catchup_requests_by_round_.erase(iter_by_round); - SL_DEBUG(base_.logger(), + SL_DEBUG(log_, "Catch-up-request with set_id={} in round={} " "has not been sent to {}: impolite to replay catch-up request", catch_up_request.voter_set_id, @@ -448,22 +401,18 @@ namespace kagome::network { }, kRecentnessDuration); - auto shared_msg = - KAGOME_EXTRACT_SHARED_CACHE(GrandpaProtocol, GrandpaMessage); - (*shared_msg) = GrandpaMessage(catch_up_request); - - stream_engine_->send(peer_id, shared_from_this(), std::move(shared_msg)); + write(peer_id, rawMessage(catch_up_request)); } void GrandpaProtocol::catchUpResponse(const libp2p::peer::PeerId &peer_id, CatchUpResponse &&catch_up_response) { - SL_DEBUG(base_.logger(), + SL_DEBUG(log_, "Send catch-up response: beginning with grandpa round number {}", catch_up_response.round_number); auto info_opt = peer_manager_->getPeerState(peer_id); if (not info_opt.has_value()) { - SL_DEBUG(base_.logger(), + SL_DEBUG(log_, "Catch-up-response with set_id={} in round={} " "has not been sent to {}: peer is not connected", catch_up_response.voter_set_id, @@ -474,7 +423,7 @@ namespace kagome::network { const auto &info = info_opt.value().get(); if (not info.set_id.has_value() or not info.round_number.has_value()) { - SL_DEBUG(base_.logger(), + SL_DEBUG(log_, "Catch-up-response with set_id={} in round={} " "has not been sent to {}: set id or round number unknown", catch_up_response.voter_set_id, @@ -485,7 +434,7 @@ namespace kagome::network { /// Impolite to send a catch up request to a peer in a new different Set ID. if (catch_up_response.voter_set_id != info.set_id) { - SL_DEBUG(base_.logger(), + SL_DEBUG(log_, "Catch-up-response with set_id={} in round={} " "has not been sent to {}: {} set id", catch_up_response.voter_set_id, @@ -497,7 +446,7 @@ namespace kagome::network { /// Avoid sending useless response (if peer is already caught up) if (catch_up_response.round_number < info.round_number) { - SL_DEBUG(base_.logger(), + SL_DEBUG(log_, "Catch-up-response with set_id={} in round={} " "has not been sent to {}: is already not actual", catch_up_response.voter_set_id, @@ -506,46 +455,58 @@ namespace kagome::network { return; } - auto shared_msg = - KAGOME_EXTRACT_SHARED_CACHE(GrandpaProtocol, GrandpaMessage); - (*shared_msg) = GrandpaMessage(std::move(catch_up_response)); + write(peer_id, rawMessage(catch_up_response)); + } - stream_engine_->send(peer_id, shared_from_this(), std::move(shared_msg)); + std::optional GrandpaProtocol::rawMessageHash( + const GrandpaMessage &message, BufferView message_raw) const { + if (boost::get(&message) + or boost::get(&message)) { + return hasher_->twox_256(message_raw); + } + return std::nullopt; } - common::Hash256 GrandpaProtocol::getHash( + GrandpaProtocol::RawMessage GrandpaProtocol::rawMessage( const GrandpaMessage &message) const { - return hasher_->twox_256(::scale::encode(message).value()); + auto message_raw = notifications::encode(message); + auto hash = rawMessageHash(message, *message_raw); + return {.raw = std::move(message_raw), .hash = hash}; } - bool GrandpaProtocol::addKnown(const PeerId &peer, - const common::Hash256 &hash) { - auto info = peer_manager_->getPeerState(peer); - return info and info->get().known_grandpa_messages.add(hash); + bool GrandpaProtocol::write(const PeerId &peer_id, RawMessage raw_message) { + if (not notifications_->peerOut(peer_id)) { + return false; + } + if (raw_message.hash) { + if (not seen_.add(peer_id, *raw_message.hash)) { + return false; + } + } + notifications_->write(peer_id, std::move(raw_message.raw)); + return true; } template - void GrandpaProtocol::broadcast(std::shared_ptr message, + void GrandpaProtocol::broadcast(const RawMessage &raw_message, const F &predicate) { constexpr size_t kAuthorities = 4; constexpr size_t kAny = 4; std::deque authorities, any; - stream_engine_->forEachPeer(shared_from_this(), [&](const PeerId &peer) { - if (auto info_ref = peer_manager_->getPeerState(peer)) { + notifications_->peersOut([&](const PeerId &peer_id, size_t) { + if (auto info_ref = peer_manager_->getPeerState(peer_id)) { auto &info = info_ref->get(); - if (not predicate(peer, info)) { - return; + if (predicate(peer_id, info)) { + (info.roles.isAuthority() ? authorities : any).emplace_back(peer_id); } - (info.roles.isAuthority() ? authorities : any).emplace_back(peer); } + return true; }); - auto hash = getHash(*message); size_t need = 0; auto loop = [&](std::deque &peers) { while (not peers.empty() and need != 0) { auto &peer = peers.back(); - if (addKnown(peer, hash)) { - stream_engine_->send(peer, shared_from_this(), message); + if (write(peer, raw_message)) { --need; } peers.pop_back(); diff --git a/core/network/impl/protocols/grandpa_protocol.hpp b/core/network/impl/protocols/grandpa_protocol.hpp index f0036a019f..bcedf5d8f3 100644 --- a/core/network/impl/protocols/grandpa_protocol.hpp +++ b/core/network/impl/protocols/grandpa_protocol.hpp @@ -6,63 +6,62 @@ #pragma once -#include "network/protocol_base.hpp" - -#include #include +#include -#include -#include -#include - -#include "consensus/grandpa/grandpa_observer.hpp" -#include "containers/objects_cache.hpp" #include "log/logger.hpp" -#include "network/impl/protocols/protocol_base_impl.hpp" -#include "network/impl/stream_engine.hpp" -#include "network/peer_manager.hpp" -#include "network/types/own_peer_info.hpp" +#include "network/notifications/protocol.hpp" +#include "network/types/grandpa_message.hpp" +#include "network/types/roles.hpp" +#include "utils/lru.hpp" #include "utils/non_copyable.hpp" namespace kagome::blockchain { - class BlockTree; class GenesisBlockHash; } // namespace kagome::blockchain +namespace kagome::consensus::grandpa { + class GrandpaObserver; +} // namespace kagome::consensus::grandpa + +namespace kagome::crypto { + class Hasher; +} // namespace kagome::crypto + namespace kagome::network { + struct OwnPeerInfo; + class PeerManager; +} // namespace kagome::network - KAGOME_DECLARE_CACHE(GrandpaProtocol, KAGOME_CACHE_UNIT(GrandpaMessage)); +namespace kagome::network { + using libp2p::PeerId; class GrandpaProtocol final - : public ProtocolBase, - public std::enable_shared_from_this, + : public std::enable_shared_from_this, + public notifications::Controller, NonCopyable, NonMovable { public: - GrandpaProtocol() = delete; - ~GrandpaProtocol() override = default; - GrandpaProtocol( - libp2p::Host &host, + const notifications::Factory ¬ifications_factory, std::shared_ptr hasher, - std::shared_ptr io_context, Roles roles, std::shared_ptr grandpa_observer, const OwnPeerInfo &own_info, - std::shared_ptr stream_engine, std::shared_ptr peer_manager, const blockchain::GenesisBlockHash &genesis_hash, std::shared_ptr scheduler); - bool start() override; - - const std::string &protocolName() const override; + // Controller + Buffer handshake() override; + bool onHandshake(const PeerId &peer_id, + size_t, + bool out, + Buffer &&handshake) override; + bool onMessage(const PeerId &peer_id, size_t, Buffer &&message) override; + void onClose(const PeerId &peer_id) override; - void onIncomingStream(std::shared_ptr stream) override; - void newOutgoingStream( - const PeerId &peer_id, - std::function>)> &&cb) - override; + void start(); void vote(network::GrandpaVote &&vote_message, std::optional peer_id); @@ -75,15 +74,18 @@ namespace kagome::network { CatchUpResponse &&catch_up_response); private: - inline static const auto kGrandpaProtocolName = "GrandpaProtocol"s; - - void onMessage(const PeerId &peer_id, GrandpaMessage message); - - common::Hash256 getHash(const GrandpaMessage &message) const; - bool addKnown(const PeerId &peer, const common::Hash256 &hash); - + static constexpr auto kGrandpaProtocolName = "GrandpaProtocol"; + + struct RawMessage { + std::shared_ptr raw; + std::optional hash; + }; + std::optional rawMessageHash(const GrandpaMessage &message, + BufferView message_raw) const; + RawMessage rawMessage(const GrandpaMessage &message) const; + bool write(const PeerId &peer_id, RawMessage message); template - void broadcast(std::shared_ptr message, const F &predicate); + void broadcast(const RawMessage &message, const F &predicate); /// Node should send catch-up requests rarely to be polite, because /// processing of them consume more enough resources. @@ -91,15 +93,15 @@ namespace kagome::network { static constexpr std::chrono::milliseconds kRecentnessDuration = std::chrono::seconds(300); - ProtocolBaseImpl base_; + log::Logger log_; + std::shared_ptr notifications_; std::shared_ptr hasher_; - std::shared_ptr io_context_; Roles roles_; std::shared_ptr grandpa_observer_; const OwnPeerInfo &own_info_; - std::shared_ptr stream_engine_; std::shared_ptr peer_manager_; std::shared_ptr scheduler_; + MapLruSet seen_; std::set> @@ -110,6 +112,8 @@ namespace kagome::network { std::set recent_catchup_requests_by_peer_; std::default_random_engine random_; + + friend class BlockAnnounceProtocol; }; } // namespace kagome::network diff --git a/core/network/impl/protocols/parachain.cpp b/core/network/impl/protocols/parachain.cpp new file mode 100644 index 0000000000..1d703109e7 --- /dev/null +++ b/core/network/impl/protocols/parachain.cpp @@ -0,0 +1,219 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "network/impl/protocols/parachain.hpp" + +#include "blockchain/genesis_block_hash.hpp" +#include "network/collation_observer.hpp" +#include "network/common.hpp" +#include "network/notifications/encode.hpp" +#include "network/peer_manager.hpp" +#include "network/peer_view.hpp" +#include "network/validation_observer.hpp" +#include "utils/try.hpp" +#include "utils/weak_macro.hpp" +#include "utils/with_type.hpp" + +// TODO(turuslan): https://github.com/qdrvm/kagome/issues/1989 +#define PROTOCOL_V1(protocol) \ + {} + +namespace kagome::network { + // https://github.com/paritytech/polkadot-sdk/blob/edf79aa972bcf2e043e18065a9bb860ecdbd1a6e/polkadot/node/network/protocol/src/peer_set.rs#L118-L119 + constexpr size_t kCollationPeersLimit = 100; + + // https://github.com/paritytech/polkadot-sdk/blob/edf79aa972bcf2e043e18065a9bb860ecdbd1a6e/polkadot/node/network/protocol/src/lib.rs#L47 + constexpr size_t kMinGossipPeers = 25; + + // https://github.com/paritytech/polkadot-sdk/blob/edf79aa972bcf2e043e18065a9bb860ecdbd1a6e/polkadot/node/network/protocol/src/peer_set.rs#L98-L99 + constexpr size_t kValidationPeersLimit = kMinGossipPeers / 2 - 1; + + inline auto makeProtocols(const ParachainProtocolInject &inject, + const std::string_view &fmt) { + return make_protocols(fmt, *inject.genesis_hash, kProtocolPrefixPolkadot); + } + + using CollationTypes = + WithType; + using ValidationTypes = + WithType; + + template + auto encodeMessage(const auto &message) { + return notifications::encode(WireMessage{message}); + } + + inline auto encodeView(const View &view) { + return encodeMessage(ViewUpdate{view}); + } + + std::pair> encodeMessage( + const VersionedValidatorProtocolMessage &message) { + size_t protocol_group = + boost::get(&message) ? 0 : 1; + auto message_raw = visit_in_place(message, [](const auto &message) { + return encodeMessage>(message); + }); + return {protocol_group, std::move(message_raw)}; + } + + ParachainProtocol::ParachainProtocol( + ParachainProtocolInject &&inject, + notifications::ProtocolsGroups protocols_groups, + size_t limit_in, + size_t limit_out) + : notifications_{inject.notifications_factory->make( + std::move(protocols_groups), limit_in, limit_out)}, + collation_versions_{CollationVersion::VStaging, CollationVersion::V1}, + roles_{inject.roles}, + peer_manager_{inject.peer_manager}, + peer_view_{std::move(inject.peer_view)}, + sync_engine_{std::move(inject.sync_engine)} {} + + Buffer ParachainProtocol::handshake() { + return scale::encode(roles_).value(); + } + + bool ParachainProtocol::onHandshake(const PeerId &peer_id, + size_t protocol_group, + bool out, + Buffer &&handshake) { + TRY_FALSE(scale::decode(handshake)); + auto state = peer_manager_->createDefaultPeerState(peer_id); + state.value().get().collation_version = + collation_versions_.at(protocol_group); + if (out) { + notifications_->write( + peer_id, protocol_group, encodeView(peer_view_->getMyView())); + } + return true; + } + + void ParachainProtocol::onClose(const PeerId &peer_id) {} + + void ParachainProtocol::start() { + if (not roles_.isAuthority()) { + return; + } + if (not sync_sub_) { + sync_sub_ = primitives::events::onSync(sync_engine_, [WEAK_SELF] { + WEAK_LOCK(self); + self->start(); + }); + return; + } + notifications_->start(weak_from_this()); + my_view_sub_ = + primitives::events::subscribe(peer_view_->getMyViewObservable(), + PeerView::EventType::kViewUpdated, + [WEAK_SELF](const ExView &event) { + WEAK_LOCK(self); + self->write(event.view); + }); + } + + void ParachainProtocol::write(const View &view) { + auto message = encodeView(view); + notifications_->peersOut([&](const PeerId &peer_id, size_t protocol_group) { + notifications_->write(peer_id, protocol_group, message); + return true; + }); + } + + template + bool ParachainProtocol::onMessage(const PeerId &peer_id, + size_t protocol_group, + Buffer &&message_raw, + Observer &observer) { + return Types::with(protocol_group, [&]() { + auto message = TRY_FALSE(scale::decode>(message_raw)); + if (auto *view = boost::get(&message)) { + peer_view_->updateRemoteView(peer_id, std::move(view->view)); + } else { + observer.onIncomingMessage(peer_id, std::move(boost::get(message))); + } + return true; + }); + } + + CollationProtocol::CollationProtocol( + ParachainProtocolInject inject, + std::shared_ptr observer) + : ParachainProtocol{ + std::move(inject), + { + makeProtocols(inject, kCollationProtocolVStaging), + PROTOCOL_V1(kCollationProtocol), + }, + kCollationPeersLimit, + 0, + }, + observer_{std::move(observer)} {} + + bool CollationProtocol::onMessage(const PeerId &peer_id, + size_t protocol_group, + Buffer &&message_raw) { + return ParachainProtocol::onMessage( + peer_id, protocol_group, std::move(message_raw), *observer_); + } + + void CollationProtocol::write(const PeerId &peer_id, + const Seconded &seconded) { + auto protocol_group = notifications_->peerOut(peer_id); + if (not protocol_group) { + return; + } + CollationTypes::with(*protocol_group, [&]() { + notifications_->write( + peer_id, *protocol_group, encodeMessage(seconded)); + }); + } + + ValidationProtocol::ValidationProtocol( + ParachainProtocolInject inject, + std::shared_ptr observer) + : ParachainProtocol{ + std::move(inject), + { + makeProtocols(inject, kValidationProtocolVStaging), + PROTOCOL_V1(kValidationProtocol), + }, + kValidationPeersLimit, + kValidationPeersLimit, + }, + observer_{std::move(observer)} {} + + bool ValidationProtocol::onMessage(const PeerId &peer_id, + size_t protocol_group, + Buffer &&message_raw) { + return ParachainProtocol::onMessage( + peer_id, protocol_group, std::move(message_raw), *observer_); + } + + void ValidationProtocol::write( + const PeerId &peer_id, + std::pair> message) { + notifications_->write(peer_id, message.first, std::move(message.second)); + } + + void ValidationProtocol::write(const BitfieldDistribution &message) { + std::vector> messages; + for (size_t i = 0; i < collation_versions_.size(); ++i) { + ValidationTypes::with(i, [&]() { + messages.emplace_back(encodeMessage(message)); + }); + } + notifications_->peersOut([&](const PeerId &peer_id, size_t protocol_group) { + notifications_->write( + peer_id, protocol_group, messages.at(protocol_group)); + return true; + }); + } + + void ValidationProtocol::reserve(const PeerId &peer_id, bool add) { + notifications_->reserve(peer_id, add); + } +} // namespace kagome::network diff --git a/core/network/impl/protocols/parachain.hpp b/core/network/impl/protocols/parachain.hpp new file mode 100644 index 0000000000..33944424f0 --- /dev/null +++ b/core/network/impl/protocols/parachain.hpp @@ -0,0 +1,130 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "network/notifications/protocol.hpp" +#include "network/types/collator_messages_vstaging.hpp" +#include "network/types/roles.hpp" +#include "primitives/event_types.hpp" + +namespace kagome::blockchain { + class BlockTree; + class GenesisBlockHash; +} // namespace kagome::blockchain + +namespace kagome::network { + class CollationObserver; + enum class CollationVersion; + class PeerManager; + class PeerView; + struct Seconded; + class ValidationObserver; + struct View; +} // namespace kagome::network + +namespace kagome::network { + using libp2p::PeerId; + + std::pair> encodeMessage( + const VersionedValidatorProtocolMessage &message); + + struct ParachainProtocolInject { + std::shared_ptr notifications_factory; + Roles roles; + std::shared_ptr genesis_hash; + std::shared_ptr peer_manager; + std::shared_ptr peer_view; + primitives::events::SyncStateSubscriptionEnginePtr sync_engine; + }; + + struct ParachainProtocol + : public std::enable_shared_from_this, + public notifications::Controller { + ParachainProtocol(ParachainProtocolInject &&inject, + notifications::ProtocolsGroups protocols_groups, + size_t limit_in, + size_t limit_out); + + // Controller + Buffer handshake() override; + bool onHandshake(const PeerId &peer_id, + size_t protocol_group, + bool out, + Buffer &&handshake) override; + void onClose(const PeerId &peer_id) override; + + void start(); + + protected: + template + bool onMessage(const PeerId &peer_id, + size_t protocol_group, + Buffer &&message, + Observer &observer); + void write(const View &view); + + // NOLINTBEGIN(cppcoreguidelines-non-private-member-variables-in-classes) + std::shared_ptr notifications_; + std::vector collation_versions_; + Roles roles_; + std::shared_ptr peer_manager_; + std::shared_ptr peer_view_; + primitives::events::SyncStateSubscriptionEnginePtr sync_engine_; + std::shared_ptr sync_sub_; + std::shared_ptr my_view_sub_; + // NOLINTEND(cppcoreguidelines-non-private-member-variables-in-classes) + }; + + class CollationProtocol final : public ParachainProtocol { + public: + CollationProtocol(ParachainProtocolInject inject, + std::shared_ptr observer); + + // Controller + bool onMessage(const PeerId &peer_id, + size_t protocol_group, + Buffer &&message) override; + + void write(const PeerId &peer_id, const Seconded &seconded); + + private: + std::shared_ptr observer_; + }; + + class ValidationProtocol final : public ParachainProtocol { + public: + ValidationProtocol(ParachainProtocolInject inject, + std::shared_ptr observer); + + // Controller + bool onMessage(const PeerId &peer_id, + size_t protocol_group, + Buffer &&message) override; + + void write(const PeerId &peer_id, + std::pair> message); + void write(const PeerId &peer_id, + const VersionedValidatorProtocolMessage &message) { + write(peer_id, encodeMessage(message)); + } + void write(const auto &peers, + const VersionedValidatorProtocolMessage &message) { + if (peers.empty()) { + return; + } + auto message_raw = encodeMessage(message); + for (auto &peer_id : peers) { + write(peer_id, message_raw); + } + } + void write(const BitfieldDistribution &message); + void reserve(const PeerId &peer_id, bool add); + + private: + std::shared_ptr observer_; + }; +} // namespace kagome::network diff --git a/core/network/impl/protocols/parachain_protocol.hpp b/core/network/impl/protocols/parachain_protocol.hpp deleted file mode 100644 index 292589db35..0000000000 --- a/core/network/impl/protocols/parachain_protocol.hpp +++ /dev/null @@ -1,208 +0,0 @@ -/** - * Copyright Quadrivium LLC - * All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include "network/protocol_base.hpp" - -#include - -#include -#include -#include - -#include "application/app_configuration.hpp" -#include "application/chain_spec.hpp" -#include "blockchain/genesis_block_hash.hpp" -#include "log/logger.hpp" -#include "network/collation_observer.hpp" -#include "network/common.hpp" -#include "network/helpers/scale_message_read_writer.hpp" -#include "network/impl/protocols/protocol_base_impl.hpp" -#include "network/impl/protocols/protocol_error.hpp" -#include "network/impl/stream_engine.hpp" -#include "network/notifications/connect_and_handshake.hpp" -#include "network/notifications/handshake_and_read_messages.hpp" -#include "network/peer_manager.hpp" -#include "network/peer_view.hpp" -#include "network/types/block_announce.hpp" -#include "network/types/collator_messages.hpp" -#include "network/types/roles.hpp" -#include "network/validation_observer.hpp" -#include "utils/non_copyable.hpp" - -namespace kagome::network { - - /** - * @brief The `ParachainProtocol` class is a template class responsible for - * handling the parachain protocol. It is a notification protocol that is - * used for both collation and validation protocols. - * @tparam Observer - observer type - * @tparam Message - message type - * @tparam kCollation - A boolean value indicating whether the protocol is for - * collation (true) or validation (false). - */ - template - class ParachainProtocol - : public ProtocolBase, - public std::enable_shared_from_this< - ParachainProtocol>, - NonCopyable, - NonMovable { - public: - using ObserverType = Observer; - using MessageType = Message; - using Self = - ParachainProtocol; - - ParachainProtocol() = delete; - ~ParachainProtocol() override = default; - - ParachainProtocol(libp2p::Host &host, - Roles roles, - const application::ChainSpec &chain_spec, - const blockchain::GenesisBlockHash &genesis_hash, - std::shared_ptr observer, - const Protocol &protocol, - std::shared_ptr peer_view, - log::Logger logger) - : base_(kParachainProtocolName, - host, - make_protocols(protocol, genesis_hash, kProtocolPrefixPolkadot), - std::move(logger)), - observer_(std::move(observer)), - roles_{roles}, - protocol_{protocol}, - peer_view_(std::move(peer_view)) { - BOOST_ASSERT(peer_view_); - } - - /** - * @brief Handles an incoming stream and starts the protocol - * - * @param stream The incoming stream. - */ - void onIncomingStream(std::shared_ptr stream) override { - BOOST_ASSERT(stream->remotePeerId().has_value()); - - // This lambda function is called when a handshake is received. - auto on_handshake = [](std::shared_ptr self, - std::shared_ptr stream, - Roles) { - self->onHandshake(stream->remotePeerId().value()); - return true; - }; - - // This lambda function is called when a message is received. - auto on_message = [peer_id = stream->remotePeerId().value()]( - std::shared_ptr self, - WireMessage message) { - visit_in_place( - std::move(message), - [&](ViewUpdate &&msg) { - SL_TRACE( - self->base_.logger(), "Received ViewUpdate from {}", peer_id); - self->peer_view_->updateRemoteView(peer_id, std::move(msg.view)); - }, - [&](MessageType &&p) { - SL_TRACE(self->base_.logger(), - "Received Collation/Validation message from {}", - peer_id); - self->observer_->onIncomingMessage(peer_id, std::move(p)); - }); - return true; - }; - - notifications::handshakeAndReadMessages>( - this->weak_from_this(), - std::move(stream), - roles_, - std::move(on_handshake), - std::move(on_message)); - } - - /** - * @brief Initiates a new outgoing stream. - * - * @param peer_info The information of the peer to connect to. - * @param cb The callback function to be called when the stream is - * established. - */ - void newOutgoingStream( - const PeerId &peer_id, - std::function>)> &&cb) - override { - SL_DEBUG(base_.logger(), - "Connect for {} stream with {}", - protocolName(), - peer_id); - - // This lambda function is called when a handshake is received. - auto on_handshake = - [cb = std::move(cb)]( - std::shared_ptr self, - outcome::result> - r) mutable { - if (not r) { - cb(r.error()); - return; - } - auto &stream = std::get<0>(r.value()); - cb(std::move(stream)); - }; - notifications::connectAndHandshake(this->weak_from_this(), - base_, - peer_id, - roles_, - std::move(on_handshake)); - } - - /** - * @brief Starts the protocol activating its logic in host's protocol - * handler. - * - * @return true if the protocol is started successfully, false otherwise. - */ - bool start() override { - return base_.start(this->weak_from_this()); - } - - /** - * @brief returns the protocol name which could be either collation or - * validation protocol - * @return the protocol name - */ - const std::string &protocolName() const override { - static std::optional protocol_name; - if (!protocol_name) { - protocol_name = base_.protocolIds().size() > 0 ? base_.protocolIds()[0] - : base_.protocolName(); - } - return protocol_name.value(); - } - - private: - void onHandshake(const PeerId &peer) { - if constexpr (kCollation) { - observer_->onIncomingCollationStream(peer, kProtoVersion); - } else { - observer_->onIncomingValidationStream(peer, kProtoVersion); - } - } - - inline static const auto kParachainProtocolName = "ParachainProtocol"s; - - ProtocolBaseImpl base_; - std::shared_ptr observer_; - Roles roles_; - const Protocol protocol_; - std::shared_ptr peer_view_; - }; - -} // namespace kagome::network diff --git a/core/network/impl/protocols/parachain_protocols.hpp b/core/network/impl/protocols/parachain_protocols.hpp deleted file mode 100644 index 268308d65b..0000000000 --- a/core/network/impl/protocols/parachain_protocols.hpp +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright Quadrivium LLC - * All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include "network/protocol_base.hpp" - -#include - -#include -#include -#include - -#include "application/app_configuration.hpp" -#include "application/chain_spec.hpp" -#include "log/logger.hpp" -#include "network/collation_observer.hpp" -#include "network/common.hpp" -#include "network/impl/protocols/parachain_protocol.hpp" -#include "network/impl/protocols/protocol_base_impl.hpp" -#include "network/impl/stream_engine.hpp" -#include "network/peer_manager.hpp" -#include "network/types/block_announce.hpp" -#include "network/types/block_announce_handshake.hpp" -#include "network/types/collator_messages_vstaging.hpp" -#include "network/types/roles.hpp" -#include "network/validation_observer.hpp" -#include "utils/non_copyable.hpp" - -namespace kagome::network { - - class CollationProtocol - : public ParachainProtocol { - public: - CollationProtocol(libp2p::Host &host, - Roles roles, - const application::ChainSpec &chain_spec, - const blockchain::GenesisBlockHash &genesis_hash, - std::shared_ptr observer, - std::shared_ptr peer_view) - : ParachainProtocol( - host, - roles, - chain_spec, - genesis_hash, - std::move(observer), - kCollationProtocol, - std::move(peer_view), - log::createLogger("CollationProtocol", "collation_protocol")){}; - }; - - class CollationProtocolVStaging - : public ParachainProtocol { - public: - CollationProtocolVStaging(libp2p::Host &host, - Roles roles, - const application::ChainSpec &chain_spec, - const blockchain::GenesisBlockHash &genesis_hash, - std::shared_ptr observer, - std::shared_ptr peer_view) - : ParachainProtocol(host, - roles, - chain_spec, - genesis_hash, - std::move(observer), - kCollationProtocolVStaging, - std::move(peer_view), - log::createLogger("CollationProtocolVStaging", - "collation_protocol_vstaging")){}; - }; - - class ValidationProtocol - : public ParachainProtocol { - public: - ValidationProtocol(libp2p::Host &host, - Roles roles, - const application::ChainSpec &chain_spec, - const blockchain::GenesisBlockHash &genesis_hash, - std::shared_ptr observer, - std::shared_ptr peer_view) - : ParachainProtocol( - host, - roles, - chain_spec, - genesis_hash, - std::move(observer), - kValidationProtocol, - std::move(peer_view), - log::createLogger("ValidationProtocol", "validation_protocol")){}; - }; - - class ValidationProtocolVStaging - : public ParachainProtocol { - public: - ValidationProtocolVStaging(libp2p::Host &host, - Roles roles, - const application::ChainSpec &chain_spec, - const blockchain::GenesisBlockHash &genesis_hash, - std::shared_ptr observer, - std::shared_ptr peer_view) - : ParachainProtocol( - host, - roles, - chain_spec, - genesis_hash, - std::move(observer), - kValidationProtocolVStaging, - std::move(peer_view), - log::createLogger("ValidationProtocolVStaging", - "validation_protocol_vstaging")){}; - }; - -} // namespace kagome::network diff --git a/core/network/impl/protocols/propagate_transactions_protocol.cpp b/core/network/impl/protocols/propagate_transactions_protocol.cpp index 60a684affa..91e042b5ce 100644 --- a/core/network/impl/protocols/propagate_transactions_protocol.cpp +++ b/core/network/impl/protocols/propagate_transactions_protocol.cpp @@ -12,9 +12,9 @@ #include "common/main_thread_pool.hpp" #include "consensus/timeline/timeline.hpp" #include "network/common.hpp" -#include "network/notifications/connect_and_handshake.hpp" -#include "network/notifications/handshake_and_read_messages.hpp" +#include "network/notifications/encode.hpp" #include "utils/pool_handler.hpp" +#include "utils/try.hpp" namespace { constexpr const char *kPropagatedTransactions = @@ -22,40 +22,40 @@ namespace { } namespace kagome::network { - - // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) - KAGOME_DEFINE_CACHE(PropagateTransactionsProtocol); + // https://github.com/paritytech/polkadot-sdk/blob/edf79aa972bcf2e043e18065a9bb860ecdbd1a6e/substrate/client/network/transactions/src/config.rs#L33 + constexpr size_t kSeenCapacity = 10240; PropagateTransactionsProtocol::PropagateTransactionsProtocol( - libp2p::Host &host, + const notifications::Factory ¬ifications_factory, Roles roles, + std::shared_ptr hasher, const application::ChainSpec &chain_spec, const blockchain::GenesisBlockHash &genesis_hash, common::MainThreadPool &main_thread_pool, std::shared_ptr timeline, std::shared_ptr extrinsic_observer, - std::shared_ptr stream_engine, std::shared_ptr extrinsic_events_engine, std::shared_ptr ext_event_key_repo) - : base_(kPropagateTransactionsProtocolName, - host, - make_protocols( - kPropagateTransactionsProtocol, genesis_hash, chain_spec), - log::createLogger(kPropagateTransactionsProtocolName, - "propagate_transactions_protocol")), + : log_{log::createLogger(kPropagateTransactionsProtocolName, + "propagate_transactions_protocol")}, + notifications_{notifications_factory.make( + {make_protocols( + kPropagateTransactionsProtocol, genesis_hash, chain_spec)}, + 0, + 0)}, roles_{roles}, + hasher_{std::move(hasher)}, main_pool_handler_{main_thread_pool.handlerStarted()}, timeline_(std::move(timeline)), extrinsic_observer_(std::move(extrinsic_observer)), - stream_engine_(std::move(stream_engine)), extrinsic_events_engine_{std::move(extrinsic_events_engine)}, - ext_event_key_repo_{std::move(ext_event_key_repo)} { + ext_event_key_repo_{std::move(ext_event_key_repo)}, + seen_{kSeenCapacity} { BOOST_ASSERT(main_pool_handler_ != nullptr); BOOST_ASSERT(timeline_ != nullptr); BOOST_ASSERT(extrinsic_observer_ != nullptr); - BOOST_ASSERT(stream_engine_ != nullptr); BOOST_ASSERT(extrinsic_events_engine_ != nullptr); BOOST_ASSERT(ext_event_key_repo_ != nullptr); @@ -67,112 +67,79 @@ namespace kagome::network { metrics_registry_->registerCounterMetric(kPropagatedTransactions); } - bool PropagateTransactionsProtocol::start() { - return base_.start(weak_from_this()); + Buffer PropagateTransactionsProtocol::handshake() { + return scale::encode(roles_).value(); } - const ProtocolName &PropagateTransactionsProtocol::protocolName() const { - return base_.protocolName(); + bool PropagateTransactionsProtocol::onHandshake(const PeerId &peer_id, + size_t, + bool, + Buffer &&handshake) { + TRY_FALSE(scale::decode(handshake)); + return true; } - void PropagateTransactionsProtocol::onIncomingStream( - std::shared_ptr stream) { - BOOST_ASSERT(stream->remotePeerId().has_value()); - auto on_handshake = [](std::shared_ptr self, - std::shared_ptr stream, - Roles) { - self->stream_engine_->addIncoming(stream, self); - return true; - }; - auto on_message = [peer_id = stream->remotePeerId().value()]( - std::shared_ptr self, - const PropagatedExtrinsics &message) { - SL_VERBOSE(self->base_.logger(), - "Received {} propagated transactions from {}", - message.extrinsics.size(), - peer_id); - - if (self->timeline_->wasSynchronized()) { - for (auto &ext : message.extrinsics) { - auto result = self->extrinsic_observer_->onTxMessage(ext); - if (result) { - SL_DEBUG(self->base_.logger(), " Received tx {}", result.value()); - } else { - SL_DEBUG(self->base_.logger(), " Rejected tx: {}", result.error()); - } + bool PropagateTransactionsProtocol::onMessage(const PeerId &peer_id, + size_t, + Buffer &&message_raw) { + auto message = TRY_FALSE(scale::decode(message_raw)); + SL_VERBOSE(log_, + "Received {} propagated transactions from {}", + message.extrinsics.size(), + peer_id); + + if (timeline_->wasSynchronized()) { + for (auto &ext : message.extrinsics) { + auto hash = hasher_->blake2b_256(ext.data); + if (not seen_.add(peer_id, hash)) { + continue; + } + auto result = extrinsic_observer_->onTxMessage(ext); + if (result) { + SL_DEBUG(log_, " Received tx {}", result.value()); + } else { + SL_DEBUG(log_, " Rejected tx: {}", result.error()); } - } else { - SL_TRACE(self->base_.logger(), - "Skipping extrinsics processing since the node was not in a " - "synchronized state yet."); } - return true; - }; - notifications::handshakeAndReadMessages( - weak_from_this(), - std::move(stream), - roles_, - std::move(on_handshake), - std::move(on_message)); + } else { + SL_TRACE(log_, + "Skipping extrinsics processing since the node was not in a " + "synchronized state yet."); + } + return true; } - void PropagateTransactionsProtocol::newOutgoingStream( - const PeerId &peer_id, - std::function>)> &&cb) { - auto on_handshake = - [cb = std::move(cb)]( - std::shared_ptr self, - outcome::result> - r) mutable { - if (not r) { - cb(r.error()); - return; - } - auto &stream = std::get<0>(r.value()); - self->stream_engine_->addOutgoing(stream, self); - cb(std::move(stream)); - }; - notifications::connectAndHandshake( - weak_from_this(), base_, peer_id, roles_, std::move(on_handshake)); + void PropagateTransactionsProtocol::onClose(const PeerId &peer_id) { + seen_.remove(peer_id); } - void PropagateTransactionsProtocol::propagateTransactions( - std::span txs) { - if (not main_pool_handler_->isInCurrentThread()) { - return main_pool_handler_->execute( - [self{shared_from_this()}, txs{std::vector(txs.begin(), txs.end())}] { - self->propagateTransactions(txs); - }); - } - SL_DEBUG( - base_.logger(), "Propagate transactions : {} extrinsics", txs.size()); + void PropagateTransactionsProtocol::start() { + notifications_->start(weak_from_this()); + } - std::vector peers; - stream_engine_->forEachPeer( - [&peers](const libp2p::peer::PeerId &peer_id, const auto &) { - peers.push_back(peer_id); - }); - if (peers.size() > 1) { // One of peers is current node itself - auto delta = peers.size() - 1; - // NOLINTNEXTLINE(cppcoreguidelines-narrowing-conversions) - metric_propagated_tx_counter_->inc(delta); - for (const auto &tx : txs) { - if (auto key = ext_event_key_repo_->get(tx.hash); key.has_value()) { - extrinsic_events_engine_->notify( - key.value(), - primitives::events::ExtrinsicLifecycleEvent::Broadcast( - key.value(), peers)); - } + void PropagateTransactionsProtocol::propagateTransaction( + primitives::Transaction tx) { + REINVOKE(*main_pool_handler_, propagateTransaction, std::move(tx)); + SL_DEBUG(log_, "Propagate transaction"); + std::vector peers; + size_t metric = 0; + auto message_raw = notifications::encode(PropagatedExtrinsics{{tx.ext}}); + notifications_->peersOut([&](const PeerId &peer_id, size_t) { + if (not seen_.add(peer_id, tx.hash)) { + return true; } - } - - for (auto &tx : txs) { - auto propagated_exts = KAGOME_EXTRACT_SHARED_CACHE( - PropagateTransactionsProtocol, PropagatedExtrinsics); - propagated_exts->extrinsics = {tx.ext}; - stream_engine_->broadcast(shared_from_this(), - propagated_exts); + notifications_->write(peer_id, message_raw); + ++metric; + peers.emplace_back(peer_id); + return true; + }); + // NOLINTNEXTLINE(cppcoreguidelines-narrowing-conversions) + metric_propagated_tx_counter_->inc(metric); + if (auto key = ext_event_key_repo_->get(tx.hash); key.has_value()) { + extrinsic_events_engine_->notify( + key.value(), + primitives::events::ExtrinsicLifecycleEvent::Broadcast(key.value(), + peers)); } } - } // namespace kagome::network diff --git a/core/network/impl/protocols/propagate_transactions_protocol.hpp b/core/network/impl/protocols/propagate_transactions_protocol.hpp index 95dc99aafa..b8e52d65ff 100644 --- a/core/network/impl/protocols/propagate_transactions_protocol.hpp +++ b/core/network/impl/protocols/propagate_transactions_protocol.hpp @@ -6,101 +6,101 @@ #pragma once -#include "network/protocol_base.hpp" - -#include - -#include -#include - -#include "application/chain_spec.hpp" -#include "containers/objects_cache.hpp" #include "log/logger.hpp" #include "metrics/metrics.hpp" #include "network/extrinsic_observer.hpp" -#include "network/impl/protocols/protocol_base_impl.hpp" -#include "network/impl/stream_engine.hpp" +#include "network/notifications/protocol.hpp" #include "network/types/propagate_transactions.hpp" #include "network/types/roles.hpp" #include "primitives/event_types.hpp" #include "subscription/extrinsic_event_key_repository.hpp" #include "subscription/subscriber.hpp" #include "subscription/subscription_engine.hpp" +#include "utils/lru.hpp" #include "utils/non_copyable.hpp" namespace kagome { class PoolHandler; } // namespace kagome +namespace kagome::application { + class ChainSpec; +} // namespace kagome::application + namespace kagome::blockchain { class GenesisBlockHash; -} +} // namespace kagome::blockchain namespace kagome::common { class MainThreadPool; -} +} // namespace kagome::common namespace kagome::consensus { class Timeline; -} +} // namespace kagome::consensus -namespace kagome::network { +namespace kagome::crypto { + class Hasher; +} // namespace kagome::crypto - KAGOME_DECLARE_CACHE(PropagateTransactionsProtocol, - KAGOME_CACHE_UNIT(PropagatedExtrinsics)); +namespace kagome::network { + using libp2p::PeerId; class PropagateTransactionsProtocol final - : public ProtocolBase, - public std::enable_shared_from_this, + : public std::enable_shared_from_this, + public notifications::Controller, NonCopyable, NonMovable { public: - PropagateTransactionsProtocol() = delete; - ~PropagateTransactionsProtocol() override = default; - PropagateTransactionsProtocol( - libp2p::Host &host, + const notifications::Factory ¬ifications_factory, Roles roles, + std::shared_ptr hasher, const application::ChainSpec &chain_spec, const blockchain::GenesisBlockHash &genesis_hash, common::MainThreadPool &main_thread_pool, std::shared_ptr timeline, std::shared_ptr extrinsic_observer, - std::shared_ptr stream_engine, std::shared_ptr extrinsic_events_engine, std::shared_ptr ext_event_key_repo); - bool start() override; - - const std::string &protocolName() const override; + // Controller + Buffer handshake() override; + bool onHandshake(const PeerId &peer_id, + size_t, + bool, + Buffer &&handshake) override; + bool onMessage(const PeerId &peer_id, size_t, Buffer &&message) override; + void onClose(const PeerId &peer_id) override; - void onIncomingStream(std::shared_ptr stream) override; - void newOutgoingStream( - const PeerId &peer_id, - std::function>)> &&cb) - override; + void start(); - void propagateTransactions(std::span txs); + void propagateTransaction(primitives::Transaction tx); private: - inline static const auto kPropagateTransactionsProtocolName = - "PropagateTransactionsProtocol"s; - ProtocolBaseImpl base_; + static constexpr auto kPropagateTransactionsProtocolName = + "PropagateTransactionsProtocol"; + + log::Logger log_; + std::shared_ptr notifications_; Roles roles_; + std::shared_ptr hasher_; std::shared_ptr main_pool_handler_; std::shared_ptr timeline_; std::shared_ptr extrinsic_observer_; - std::shared_ptr stream_engine_; std::shared_ptr extrinsic_events_engine_; std::shared_ptr ext_event_key_repo_; + MapLruSet seen_; // Metrics metrics::RegistryPtr metrics_registry_ = metrics::createRegistry(); metrics::Counter *metric_propagated_tx_counter_; + + friend class BlockAnnounceProtocol; }; } // namespace kagome::network diff --git a/core/network/impl/protocols/protocol_fetch_chunk.hpp b/core/network/impl/protocols/protocol_fetch_chunk.hpp index e8f6bb2b2c..183e4c7c92 100644 --- a/core/network/impl/protocols/protocol_fetch_chunk.hpp +++ b/core/network/impl/protocols/protocol_fetch_chunk.hpp @@ -16,8 +16,8 @@ #include "blockchain/genesis_block_hash.hpp" #include "log/logger.hpp" #include "network/common.hpp" +#include "network/helpers/scale_message_read_writer.hpp" #include "network/impl/protocols/request_response_protocol.hpp" -#include "network/impl/stream_engine.hpp" #include "network/peer_manager.hpp" #include "parachain/validator/parachain_processor.hpp" #include "utils/non_copyable.hpp" diff --git a/core/network/impl/protocols/protocol_fetch_chunk_obsolete.hpp b/core/network/impl/protocols/protocol_fetch_chunk_obsolete.hpp index 3a5eac8ce7..879a538dd3 100644 --- a/core/network/impl/protocols/protocol_fetch_chunk_obsolete.hpp +++ b/core/network/impl/protocols/protocol_fetch_chunk_obsolete.hpp @@ -17,7 +17,6 @@ #include "log/logger.hpp" #include "network/common.hpp" #include "network/impl/protocols/request_response_protocol.hpp" -#include "network/impl/stream_engine.hpp" #include "parachain/validator/parachain_processor.hpp" #include "utils/non_copyable.hpp" diff --git a/core/network/impl/protocols/protocol_req_collation.cpp b/core/network/impl/protocols/protocol_req_collation.cpp index c74bd60b00..84c0694e60 100644 --- a/core/network/impl/protocols/protocol_req_collation.cpp +++ b/core/network/impl/protocols/protocol_req_collation.cpp @@ -8,6 +8,7 @@ #include "blockchain/genesis_block_hash.hpp" #include "network/common.hpp" +#include "network/helpers/scale_message_read_writer.hpp" #include "network/impl/protocols/request_response_protocol.hpp" #include "utils/non_copyable.hpp" diff --git a/core/network/impl/protocols/protocol_req_collation.hpp b/core/network/impl/protocols/protocol_req_collation.hpp index d10dbe94af..6e97c23d84 100644 --- a/core/network/impl/protocols/protocol_req_collation.hpp +++ b/core/network/impl/protocols/protocol_req_collation.hpp @@ -16,7 +16,6 @@ #include "application/app_configuration.hpp" #include "application/chain_spec.hpp" #include "log/logger.hpp" -#include "network/impl/stream_engine.hpp" #include "network/peer_manager.hpp" #include "network/protocols/req_collation_protocol.hpp" #include "network/types/collator_messages_vstaging.hpp" diff --git a/core/network/impl/protocols/protocol_req_pov.cpp b/core/network/impl/protocols/protocol_req_pov.cpp index 4a1828eef7..d7eee420ee 100644 --- a/core/network/impl/protocols/protocol_req_pov.cpp +++ b/core/network/impl/protocols/protocol_req_pov.cpp @@ -8,6 +8,7 @@ #include "blockchain/genesis_block_hash.hpp" #include "network/common.hpp" +#include "network/helpers/scale_message_read_writer.hpp" #include "network/impl/protocols/request_response_protocol.hpp" #include "utils/non_copyable.hpp" @@ -24,15 +25,15 @@ namespace kagome::network { const blockchain::GenesisBlockHash &genesis_hash, std::shared_ptr observer) : RequestResponseProtocolImpl< - RequestPov, - ResponsePov, - ScaleMessageReadWriter>{kReqPovProtocolName, - host, - make_protocols(kReqPovProtocol, - genesis_hash, - kProtocolPrefixPolkadot), - log::createLogger(kReqPovProtocolName, - "req_pov_protocol")}, + RequestPov, + ResponsePov, + ScaleMessageReadWriter>{kReqPovProtocolName, + host, + make_protocols(kReqPovProtocol, + genesis_hash, + kProtocolPrefixPolkadot), + log::createLogger(kReqPovProtocolName, + "req_pov_protocol")}, observer_{std::move(observer)} {} protected: @@ -72,7 +73,7 @@ namespace kagome::network { const blockchain::GenesisBlockHash &genesis_hash, std::shared_ptr observer) : impl_{std::make_shared( - host, chain_spec, genesis_hash, std::move(observer))} {} + host, chain_spec, genesis_hash, std::move(observer))} {} const Protocol &ReqPovProtocol::protocolName() const { BOOST_ASSERT(impl_ && !!"ReqPovProtocolImpl must be initialized!"); diff --git a/core/network/impl/protocols/protocol_req_pov.hpp b/core/network/impl/protocols/protocol_req_pov.hpp index ceba2f6198..8ee8f62337 100644 --- a/core/network/impl/protocols/protocol_req_pov.hpp +++ b/core/network/impl/protocols/protocol_req_pov.hpp @@ -16,7 +16,6 @@ #include "application/app_configuration.hpp" #include "application/chain_spec.hpp" #include "log/logger.hpp" -#include "network/impl/stream_engine.hpp" #include "network/peer_manager.hpp" #include "network/protocols/req_pov_protocol.hpp" #include "utils/non_copyable.hpp" diff --git a/core/network/impl/protocols/send_dispute_protocol.hpp b/core/network/impl/protocols/send_dispute_protocol.hpp index 20395a09c9..aca04a9138 100644 --- a/core/network/impl/protocols/send_dispute_protocol.hpp +++ b/core/network/impl/protocols/send_dispute_protocol.hpp @@ -21,7 +21,7 @@ #include "log/logger.hpp" #include "network/common.hpp" #include "network/dispute_request_observer.hpp" -#include "network/impl/stream_engine.hpp" +#include "network/helpers/scale_message_read_writer.hpp" #include "network/types/dispute_messages.hpp" #include "utils/non_copyable.hpp" diff --git a/core/network/impl/router_libp2p.cpp b/core/network/impl/router_libp2p.cpp index 2283179743..7ca309191c 100644 --- a/core/network/impl/router_libp2p.cpp +++ b/core/network/impl/router_libp2p.cpp @@ -10,11 +10,12 @@ #include #include "common/main_thread_pool.hpp" #include "network/impl/protocols/beefy_justification_protocol.hpp" +#include "network/impl/protocols/beefy_protocol_impl.hpp" #include "network/impl/protocols/block_announce_protocol.hpp" #include "network/impl/protocols/fetch_attested_candidate.hpp" #include "network/impl/protocols/grandpa_protocol.hpp" #include "network/impl/protocols/light.hpp" -#include "network/impl/protocols/parachain_protocols.hpp" +#include "network/impl/protocols/parachain.hpp" #include "network/impl/protocols/propagate_transactions_protocol.hpp" #include "network/impl/protocols/protocol_fetch_available_data.hpp" #include "network/impl/protocols/protocol_fetch_chunk.hpp" @@ -22,10 +23,10 @@ #include "network/impl/protocols/protocol_req_collation.hpp" #include "network/impl/protocols/protocol_req_pov.hpp" #include "network/impl/protocols/send_dispute_protocol.hpp" -#include "network/protocols/beefy_protocol.hpp" #include "network/protocols/state_protocol.hpp" #include "network/protocols/sync_protocol.hpp" #include "network/types/bootstrap_nodes.hpp" +#include "network/types/own_peer_info.hpp" #include "network/warp/protocol.hpp" namespace kagome::network { @@ -41,14 +42,12 @@ namespace kagome::network { LazySPtr sync_protocol, LazySPtr state_protocol, LazySPtr warp_protocol, - LazySPtr beefy_protocol, + LazySPtr beefy_protocol, LazySPtr beefy_justifications_protocol, LazySPtr light_protocol, LazySPtr propagate_transactions_protocol, LazySPtr validation_protocol, LazySPtr collation_protocol, - LazySPtr collation_protocol_vstaging, - LazySPtr validation_protocol_vstaging, LazySPtr req_collation_protocol, LazySPtr req_pov_protocol, LazySPtr fetch_chunk_protocol, @@ -74,8 +73,6 @@ namespace kagome::network { propagate_transactions_protocol_(propagate_transactions_protocol), validation_protocol_(validation_protocol), collation_protocol_(collation_protocol), - collation_protocol_vstaging_(collation_protocol_vstaging), - validation_protocol_vstaging_(validation_protocol_vstaging), req_collation_protocol_(req_collation_protocol), req_pov_protocol_(req_pov_protocol), fetch_chunk_protocol_(fetch_chunk_protocol), @@ -123,13 +120,8 @@ namespace kagome::network { lazyStart(light_protocol_); lazyStart(propagate_transactions_protocol_); - // TODO(iceseer): https://github.com/qdrvm/kagome/issues/1989 - // should be uncommented when this task will be implemented - // lazyStart(collation_protocol_); - // lazyStart(validation_protocol_); - - lazyStart(collation_protocol_vstaging_); - lazyStart(validation_protocol_vstaging_); + lazyStart(collation_protocol_); + lazyStart(validation_protocol_); lazyStart(req_collation_protocol_); lazyStart(req_pov_protocol_); lazyStart(fetch_chunk_protocol_); @@ -244,21 +236,11 @@ namespace kagome::network { return collation_protocol_.get(); } - std::shared_ptr - RouterLibp2p::getCollationProtocolVStaging() const { - return collation_protocol_vstaging_.get(); - } - std::shared_ptr RouterLibp2p::getValidationProtocol() const { return validation_protocol_.get(); } - std::shared_ptr - RouterLibp2p::getValidationProtocolVStaging() const { - return validation_protocol_vstaging_.get(); - } - std::shared_ptr RouterLibp2p::getReqCollationProtocol() const { return req_collation_protocol_.get(); diff --git a/core/network/impl/router_libp2p.hpp b/core/network/impl/router_libp2p.hpp index 8721848eaf..5fbf59c578 100644 --- a/core/network/impl/router_libp2p.hpp +++ b/core/network/impl/router_libp2p.hpp @@ -13,11 +13,11 @@ namespace libp2p { struct Host; -} +} // namespace libp2p namespace libp2p::multi { class Multiaddress; -} +} // namespace libp2p::multi namespace kagome { class PoolHandler; @@ -31,13 +31,14 @@ namespace kagome::application { namespace kagome::blockchain { class BlockStorage; -} +} // namespace kagome::blockchain namespace kagome::common { class MainThreadPool; -} +} // namespace kagome::common namespace kagome::network { + class BeefyProtocolImpl; struct OwnPeerInfo; struct BootstrapNodes; class WarpProtocol; @@ -61,14 +62,12 @@ namespace kagome::network { LazySPtr sync_protocol, LazySPtr state_protocol, LazySPtr warp_protocol, - LazySPtr beefy_protocol, + LazySPtr beefy_protocol, LazySPtr beefy_justifications_protocol, LazySPtr light_protocol, LazySPtr propagate_transactions_protocol, LazySPtr validation_protocol, LazySPtr collation_protocol, - LazySPtr collation_protocol_vstaging, - LazySPtr validation_protocol_vstaging, LazySPtr req_collation_protocol, LazySPtr req_pov_protocol, LazySPtr fetch_chunk_protocol, @@ -98,11 +97,7 @@ namespace kagome::network { getPropagateTransactionsProtocol() const override; std::shared_ptr getCollationProtocol() const override; - std::shared_ptr getCollationProtocolVStaging() - const override; std::shared_ptr getValidationProtocol() const override; - std::shared_ptr getValidationProtocolVStaging() - const override; std::shared_ptr getReqCollationProtocol() const override; std::shared_ptr getReqPovProtocol() const override; @@ -146,7 +141,7 @@ namespace kagome::network { LazySPtr sync_protocol_; LazySPtr state_protocol_; LazySPtr warp_protocol_; - LazySPtr beefy_protocol_; + LazySPtr beefy_protocol_; LazySPtr beefy_justifications_protocol_; LazySPtr light_protocol_; @@ -154,8 +149,6 @@ namespace kagome::network { LazySPtr validation_protocol_; LazySPtr collation_protocol_; - LazySPtr collation_protocol_vstaging_; - LazySPtr validation_protocol_vstaging_; LazySPtr req_collation_protocol_; LazySPtr req_pov_protocol_; LazySPtr fetch_chunk_protocol_; diff --git a/core/network/impl/stream_engine.cpp b/core/network/impl/stream_engine.cpp deleted file mode 100644 index bbf00c3772..0000000000 --- a/core/network/impl/stream_engine.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/** - * Copyright Quadrivium LLC - * All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "network/impl/stream_engine.hpp" - -namespace kagome::network { - - void StreamEngine::add(std::shared_ptr stream, - const std::shared_ptr &protocol, - Direction direction) { - BOOST_ASSERT(protocol != nullptr); - BOOST_ASSERT(stream != nullptr); - - auto peer_id = stream->remotePeerId().value(); - auto dir = static_cast(direction); - const bool is_incoming = - (dir & static_cast(Direction::INCOMING)) != 0; - const bool is_outgoing = - (dir & static_cast(Direction::OUTGOING)) != 0; - - SL_TRACE(logger_, - "Add stream for peer.(peer={}, protocol={})", - peer_id, - protocol->protocolName()); - - streams_.exclusiveAccess([&](PeerMap &streams) { - bool existing = false; - forPeerProtocol( - peer_id, streams, protocol, [&](const auto &type, auto &descr) { - existing = true; - if (is_incoming) { - uploadStream( - descr.incoming.stream, stream, protocol, Direction::INCOMING); - } - if (is_outgoing) { - uploadStream( - descr.outgoing.stream, stream, protocol, Direction::OUTGOING); - } - }); - - if (not existing) { - auto &proto_map = streams[peer_id]; - proto_map.emplace(protocol, - ProtocolDescr{protocol, - is_incoming ? stream : nullptr, - is_outgoing ? stream : nullptr}); - SL_DEBUG(logger_, - "Added {} {} stream with peer {}", - direction == Direction::INCOMING ? "incoming" - : direction == Direction::OUTGOING ? "outgoing" - : "bidirectional", - protocol->protocolName(), - peer_id); - } - }); - } - - void StreamEngine::reserveStreams( - const PeerId &peer_id, const std::shared_ptr &protocol) { - BOOST_ASSERT(protocol != nullptr); - const auto reserved = streams_.exclusiveAccess([&](auto &streams) { - return streams[peer_id].emplace(protocol, ProtocolDescr{protocol}).second; - }); - - if (reserved) { - SL_DEBUG(logger_, - "Reserved {} stream with peer {}", - protocol->protocolName(), - peer_id); - } - } - - void StreamEngine::del(const PeerId &peer_id) { - SL_TRACE(logger_, "Remove all streams from peer.(peer={})", peer_id); - streams_.exclusiveAccess([&](auto &streams) { - if (auto it = streams.find(peer_id); it != streams.end()) { - for (auto &protocol_it : it->second) { - auto &descr = protocol_it.second; - if (descr.incoming.stream) { - descr.incoming.stream->reset(); - } - if (descr.outgoing.stream) { - descr.outgoing.stream->reset(); - } - } - streams.erase(it); - } - }); - } - - void StreamEngine::del(const PeerId &peer_id, - const std::shared_ptr &protocol) { - SL_TRACE(logger_, - "Remove {} streams from peer.(peer={})", - protocol->protocolName(), - peer_id); - streams_.exclusiveAccess([&](auto &streams) { - if (auto it = streams.find(peer_id); it != streams.end()) { - auto &protocols = it->second; - auto protocol_it = protocols.find(protocol); - if (protocol_it != protocols.end()) { - auto &descr = protocol_it->second; - if (descr.incoming.stream) { - descr.incoming.stream->reset(); - } - if (descr.outgoing.stream) { - descr.outgoing.stream->reset(); - } - protocols.erase(protocol_it); - } - if (protocols.empty()) { - streams.erase(it); - } - } - }); - } - - bool StreamEngine::reserveOutgoing( - const PeerId &peer_id, const std::shared_ptr &protocol) { - BOOST_ASSERT(protocol); - return streams_.exclusiveAccess([&](PeerMap &streams) { - auto &proto_map = streams[peer_id]; - auto [it, _] = proto_map.emplace(protocol, ProtocolDescr{protocol}); - return it->second.tryReserveOutgoing(); - }); - } - - void StreamEngine::dropReserveOutgoing( - const PeerId &peer_id, const std::shared_ptr &protocol) { - BOOST_ASSERT(protocol); - return streams_.exclusiveAccess([&](auto &streams) { - forPeerProtocol( - peer_id, streams, protocol, [&](const auto &, ProtocolDescr &descr) { - return descr.dropReserved(); - }); - }); - } - - void StreamEngine::uploadStream(std::shared_ptr &dst, - const std::shared_ptr &src, - const std::shared_ptr &protocol, - Direction direction) { - BOOST_ASSERT(src); - // Skip the same stream - if (dst.get() == src.get()) { - return; - } - - bool replaced = false; - // Reset previous stream if any - if (dst) { - if (direction == Direction::INCOMING) { - dst->close([](outcome::result) {}); - } else { - dst->reset(); - } - replaced = true; - } - - dst = src; - SL_DEBUG(logger_, - "{} {} stream with peer {} was {}", - direction == Direction::INCOMING ? "Incoming" : "Outgoing", - protocol->protocolName(), - dst->remotePeerId().has_value() - ? fmt::format("{}", dst->remotePeerId().value()) - : "without PeerId", - replaced ? "replaced" : "stored"); - } - - void StreamEngine::openOutgoingStream( - const PeerId &peer_id, - const std::shared_ptr &protocol, - ProtocolDescr &descr) { - if (descr.tryReserveOutgoing()) { - protocol->newOutgoingStream( - peer_id, - [wp(weak_from_this()), protocol, peer_id](auto &&stream_res) mutable { - auto self = wp.lock(); - if (not self) { - return; - } - - if (!stream_res) { - SL_DEBUG(self->logger_, - "Could not send message to new {} stream with {}: {}", - protocol->protocolName(), - peer_id, - stream_res.error()); - - self->streams_.exclusiveAccess([&](auto &streams) { - self->forPeerProtocol( - peer_id, streams, protocol, [&](const auto &, auto &descr) { - descr.deferred_messages.clear(); - descr.dropReserved(); - }); - }); - - if (stream_res - == outcome::failure( - std::make_error_code(std::errc::not_connected))) { - self->reputation_repository_->changeForATime( - peer_id, - reputation::cost::UNEXPECTED_DISCONNECT, - kDownVoteByDisconnectionExpirationTimeout); - } - - return; - } - - auto &stream = stream_res.value(); - self->streams_.exclusiveAccess([&](auto &streams) { - [[maybe_unused]] bool existing = false; - self->forPeerProtocol( - peer_id, streams, protocol, [&](const auto &, auto &descr) { - existing = true; - self->uploadStream(descr.outgoing.stream, - stream, - protocol, - Direction::OUTGOING); - descr.dropReserved(); - - while (!descr.deferred_messages.empty()) { - auto &msg = descr.deferred_messages.front(); - msg(stream); - descr.deferred_messages.pop_front(); - } - }); - BOOST_ASSERT(existing); - }); - }); - } - } -} // namespace kagome::network diff --git a/core/network/impl/stream_engine.hpp b/core/network/impl/stream_engine.hpp deleted file mode 100644 index 562ab1d174..0000000000 --- a/core/network/impl/stream_engine.hpp +++ /dev/null @@ -1,367 +0,0 @@ -/** - * Copyright Quadrivium LLC - * All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -#if defined(BACKWARD_HAS_BACKTRACE) -#include -#endif - -#include "libp2p/connection/stream.hpp" -#include "libp2p/host/host.hpp" -#include "libp2p/peer/peer_info.hpp" -#include "libp2p/peer/protocol.hpp" -#include "log/logger.hpp" -#include "network/helpers/scale_message_read_writer.hpp" -#include "network/protocol_base.hpp" -#include "network/reputation_repository.hpp" -#include "subscription/subscriber.hpp" -#include "subscription/subscription_engine.hpp" -#include "utils/safe_object.hpp" - -namespace kagome::network { - /** - * Is the manager class to manipulate streams. It supports next structure - * Peer - * ` ProtocolName_0 - * ` ProtocolPtr_0, - * Incoming_Stream_0 - * Outgoing_Stream_0 - * MessagesQueue for creating outgoing stream - */ - struct StreamEngine final : std::enable_shared_from_this { - using PeerInfo = libp2p::peer::PeerInfo; - using PeerId = libp2p::peer::PeerId; - using Protocol = libp2p::peer::ProtocolName; - using Stream = libp2p::connection::Stream; - using StreamEnginePtr = std::shared_ptr; - - static constexpr auto kDownVoteByDisconnectionExpirationTimeout = - std::chrono::seconds(30); - - enum class Direction : uint8_t { - INCOMING = 1, - OUTGOING = 2, - }; - - public: - StreamEngine(const StreamEngine &) = delete; - StreamEngine &operator=(const StreamEngine &) = delete; - - StreamEngine(StreamEngine &&) = delete; - StreamEngine &operator=(StreamEngine &&) = delete; - - ~StreamEngine() = default; - explicit StreamEngine( - std::shared_ptr reputation_repository) - : reputation_repository_(std::move(reputation_repository)), - logger_{log::createLogger("StreamEngine", "network")} {} - - void add(std::shared_ptr stream, - const std::shared_ptr &protocol, - Direction direction); - - void addIncoming(std::shared_ptr stream, - const std::shared_ptr &protocol) { - add(std::move(stream), protocol, Direction::INCOMING); - } - - void addOutgoing(std::shared_ptr stream, - const std::shared_ptr &protocol) { - if (auto res = stream->remotePeerId(); res.has_value()) { - SL_TRACE(logger_, - "Add outgoing protocol.(protocol={}, peer_id={})", - protocol->protocolName(), - res.value()); - } - add(std::move(stream), protocol, Direction::OUTGOING); - } - - void reserveStreams(const PeerId &peer_id, - const std::shared_ptr &protocol); - - void del(const PeerId &peer_id); - - void del(const PeerId &peer_id, - const std::shared_ptr &protocol); - - bool reserveOutgoing(const PeerId &peer_id, - const std::shared_ptr &protocol); - - void dropReserveOutgoing(const PeerId &peer_id, - const std::shared_ptr &protocol); - - template - void send(const PeerId &peer_id, - const std::shared_ptr &protocol, - std::shared_ptr msg) { - BOOST_ASSERT(msg != nullptr); - BOOST_ASSERT(protocol != nullptr); - - bool was_sent = false; - streams_.sharedAccess([&](const auto &streams) { - forPeerProtocol( - peer_id, streams, protocol, [&](auto type, auto const &descr) { - if (descr.hasActiveOutgoing()) { - send(peer_id, protocol, descr.outgoing.stream, msg); - was_sent = true; - } - }); - }); - - if (not was_sent) { - updateStream(peer_id, protocol, msg); - } - } - - template - void broadcast( - const std::shared_ptr &protocol, - const std::shared_ptr &msg, - const std::function &predicate) { - BOOST_ASSERT(msg != nullptr); - BOOST_ASSERT(protocol != nullptr); - - forEachPeer([&](const auto &peer_id, auto &proto_map) { - if (predicate(peer_id)) { - forProtocol(proto_map, protocol, [&](ProtocolDescr &descr) { - SL_TRACE(logger_, - "Sending msg to peer.(protocol={}, peer={})", - protocol->protocolName(), - peer_id); - if (descr.hasActiveOutgoing()) { - SL_TRACE( - logger_, - "Has active outgoing. Direct send.(protocol={}, peer={})", - protocol->protocolName(), - peer_id); - send(peer_id, protocol, descr.outgoing.stream, msg); - } else { - SL_TRACE( - logger_, - "No active outgoing. Reopen outgoing stream.(protocol={}, " - "peer={})", - protocol->protocolName(), - peer_id); - descr.deferred_messages.push_back( - [weak_self{weak_from_this()}, msg, peer_id, protocol]( - auto stream) { - if (auto self = weak_self.lock()) { - SL_TRACE(self->logger_, - "Send deffered messages.(protocol={}, peer={})", - protocol->protocolName(), - peer_id); - self->send(peer_id, protocol, stream, msg); - } - }); - openOutgoingStream(peer_id, protocol, descr); - } - }); - } - }); - } - - template - void broadcast(const std::shared_ptr &protocol, - const std::shared_ptr &msg) { - static const std::function any = - [](const PeerId &) { return true; }; - broadcast(protocol, msg, any); - } - - template - void forEachPeer(F &&f) { - streams_.exclusiveAccess([&](auto &streams) { - for (auto &[peer_id, protocol_map] : streams) { - std::forward(f)(peer_id, protocol_map); - } - }); - } - - template - void forEachPeer(F &&f) const { - streams_.sharedAccess([&](const auto &streams) { - for (auto const &[peer_id, protocol_map] : streams) { - std::forward(f)(peer_id, protocol_map); - } - }); - } - - template - void forEachPeer(const std::shared_ptr &protocol, - const F &f) const { - forEachPeer([&](const PeerId &peer, const ProtocolMap &protocols) { - if (protocols.find(protocol) != protocols.end()) { - f(peer); - } - }); - } - - private: - struct ProtocolDescr { - std::shared_ptr protocol; - log::Logger logger = log::createLogger("ProtoDescription", "network"); - - struct { - std::shared_ptr stream; - } incoming; - - struct { - std::shared_ptr stream; - bool reserved = false; - } outgoing; - - std::deque)>> - deferred_messages; - - public: - explicit ProtocolDescr(std::shared_ptr proto) - : protocol{std::move(proto)} {} - ProtocolDescr(std::shared_ptr proto, - std::shared_ptr incoming_stream, - std::shared_ptr outgoing_stream) - : protocol{std::move(proto)}, - incoming{std::move(incoming_stream)}, - outgoing{std::move(outgoing_stream)} {} - - /** - * Returns if descriptor contains active outgoing stream. - */ - bool hasActiveOutgoing() const { - return outgoing.stream and not outgoing.stream->isClosed(); - } - - /** - * Sets the flag that outgoing stream establishing. To prevent the - * situation of multiple streams to a single peer at the same time. - */ - bool tryReserveOutgoing() { - if (outgoing.reserved or hasActiveOutgoing()) { - return false; - } - - outgoing.reserved = true; - // bt(); - return true; - } - - bool isOutgoingReserved() const { - return outgoing.reserved; - } - - /** - * Drops the flag that outgoing stream establishing. - */ - void dropReserved() { - // BOOST_ASSERT(outgoing.reserved); - outgoing.reserved = false; - // bt(); - } - - /** - * Returns if descriptor contains active incoming stream. - */ - [[maybe_unused]] bool hasActiveIncoming() const { - return incoming.stream and not incoming.stream->isClosed(); - } - }; - - using ProtocolMap = - std::unordered_map, struct ProtocolDescr>; - using PeerMap = std::unordered_map; - - void uploadStream(std::shared_ptr &dst, - const std::shared_ptr &src, - const std::shared_ptr &protocol, - Direction direction); - - template - void send(const PeerId &peer_id, - const std::shared_ptr &protocol, - std::shared_ptr stream, - const std::shared_ptr &msg) { - BOOST_ASSERT(stream != nullptr); - - auto read_writer = std::make_shared(stream); - read_writer->write( - *msg, - [wp(weak_from_this()), peer_id, protocol, msg, stream](auto &&res) { - if (auto self = wp.lock()) { - if (res.has_value()) { - SL_TRACE(self->logger_, - "Message sent to {} stream with {}", - protocol->protocolName(), - peer_id); - } else { - SL_TRACE(self->logger_, - "Could not send message to {} stream with {}: {}", - protocol->protocolName(), - peer_id, - res.error()); - stream->reset(); - } - } - }); - } - - template - static void forProtocol(PM &proto_map, - const std::shared_ptr &protocol, - F &&f) { - if (auto it = proto_map.find(protocol); it != proto_map.end()) { - auto &descr = it->second; - std::forward(f)(descr); - } - } - - template - static void forPeerProtocol(const PeerId &peer_id, - PM &streams, - const std::shared_ptr &protocol, - F &&f) { - if (auto it = streams.find(peer_id); it != streams.end()) { - forProtocol(it->second, protocol, [&](auto &descr) { - std::forward(f)(it->second, descr); - }); - } - } - - void openOutgoingStream(const PeerId &peer_id, - const std::shared_ptr &protocol, - ProtocolDescr &descr); - - template - void updateStream(const PeerId &peer_id, - const std::shared_ptr &protocol, - std::shared_ptr msg) { - streams_.exclusiveAccess([&](auto &streams) { - forPeerProtocol(peer_id, streams, protocol, [&](auto, auto &descr) { - descr.deferred_messages.push_back( - [wp(weak_from_this()), peer_id, protocol, msg(std::move(msg))]( - std::shared_ptr stream) { - if (auto self = wp.lock()) { - self->send(peer_id, protocol, stream, msg); - } - }); - openOutgoingStream(peer_id, protocol, descr); - }); - }); - } - - std::shared_ptr reputation_repository_; - log::Logger logger_; - - SafeObject streams_; - }; - -} // namespace kagome::network diff --git a/core/network/impl/sync_protocol_observer_impl.cpp b/core/network/impl/sync_protocol_observer_impl.cpp index 7f91b43004..50b802bf97 100644 --- a/core/network/impl/sync_protocol_observer_impl.cpp +++ b/core/network/impl/sync_protocol_observer_impl.cpp @@ -30,16 +30,13 @@ namespace kagome::network { SyncProtocolObserverImpl::SyncProtocolObserverImpl( std::shared_ptr block_tree, std::shared_ptr blocks_headers, - std::shared_ptr beefy, - std::shared_ptr peer_manager) + std::shared_ptr beefy) : block_tree_{std::move(block_tree)}, blocks_headers_{std::move(blocks_headers)}, beefy_{std::move(beefy)}, - peer_manager_{std::move(peer_manager)}, log_(log::createLogger("SyncProtocolObserver", "network")) { BOOST_ASSERT(block_tree_); BOOST_ASSERT(blocks_headers_); - BOOST_ASSERT(peer_manager_); } outcome::result @@ -71,7 +68,6 @@ namespace kagome::network { return response; } const auto &chain_hash = chain_hash_res.value(); - peer_manager_->reserveStatusStreams(peer_id); // thirdly, fill the resulting response with data, which we were asked for fillBlocksResponse(request, response, chain_hash); diff --git a/core/network/impl/sync_protocol_observer_impl.hpp b/core/network/impl/sync_protocol_observer_impl.hpp index 9f287e4958..eb96a760d5 100644 --- a/core/network/impl/sync_protocol_observer_impl.hpp +++ b/core/network/impl/sync_protocol_observer_impl.hpp @@ -14,7 +14,6 @@ #include "blockchain/block_header_repository.hpp" #include "blockchain/block_tree.hpp" #include "log/logger.hpp" -#include "network/peer_manager.hpp" #include "network/types/own_peer_info.hpp" #include "primitives/common.hpp" @@ -32,10 +31,7 @@ namespace kagome::network { SyncProtocolObserverImpl( std::shared_ptr block_tree, std::shared_ptr blocks_headers, - std::shared_ptr beefy, - std::shared_ptr peer_manager); - - ~SyncProtocolObserverImpl() override = default; + std::shared_ptr beefy); outcome::result onBlocksRequest( const BlocksRequest &request, @@ -56,7 +52,6 @@ namespace kagome::network { std::shared_ptr beefy_; mutable std::unordered_set requested_ids_; - std::shared_ptr peer_manager_; log::Logger log_; }; diff --git a/core/network/impl/transactions_transmitter_impl.cpp b/core/network/impl/transactions_transmitter_impl.cpp index ef36fe68c7..8dac17187a 100644 --- a/core/network/impl/transactions_transmitter_impl.cpp +++ b/core/network/impl/transactions_transmitter_impl.cpp @@ -15,11 +15,11 @@ namespace kagome::network { std::shared_ptr router) : router_(std::move(router)) {} - void TransactionsTransmitterImpl::propagateTransactions( - std::span txs) { + void TransactionsTransmitterImpl::propagateTransaction( + primitives::Transaction tx) { auto protocol = router_->getPropagateTransactionsProtocol(); BOOST_ASSERT_MSG(protocol, "Router did not provide propagate transactions protocol"); - protocol->propagateTransactions(txs); + protocol->propagateTransaction(std::move(tx)); } } // namespace kagome::network diff --git a/core/network/impl/transactions_transmitter_impl.hpp b/core/network/impl/transactions_transmitter_impl.hpp index 5bd7c6fdf0..1016a0c678 100644 --- a/core/network/impl/transactions_transmitter_impl.hpp +++ b/core/network/impl/transactions_transmitter_impl.hpp @@ -15,8 +15,7 @@ namespace kagome::network { public: TransactionsTransmitterImpl(std::shared_ptr router); - void propagateTransactions( - std::span txs) override; + void propagateTransaction(primitives::Transaction tx) override; private: std::shared_ptr router_; diff --git a/core/network/notifications/encode.hpp b/core/network/notifications/encode.hpp new file mode 100644 index 0000000000..532bc0c230 --- /dev/null +++ b/core/network/notifications/encode.hpp @@ -0,0 +1,19 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#include + +#include "common/buffer.hpp" + +namespace kagome::network::notifications { + std::shared_ptr encode(const auto &message) { + return std::make_shared(scale::encode(message).value()); + } +} // namespace kagome::network::notifications diff --git a/core/network/notifications/protocol.cpp b/core/network/notifications/protocol.cpp new file mode 100644 index 0000000000..0a58cfcd62 --- /dev/null +++ b/core/network/notifications/protocol.cpp @@ -0,0 +1,457 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "common/main_thread_pool.hpp" +#include "network/helpers/new_stream.hpp" +#include "network/notifications/handshake.hpp" +#include "network/notifications/protocol.hpp" +#include "utils/map_entry.hpp" +#include "utils/weak_macro.hpp" + +namespace kagome::network::notifications { + constexpr auto kTimer = std::chrono::seconds{1}; + constexpr auto kBackoffMin = std::chrono::seconds{5}; + constexpr auto kBackoffMax = std::chrono::seconds{10}; + + StreamInfo::StreamInfo(const ProtocolsGroups &protocols_groups, + const StreamAndProtocol &info) + : protocol_group{}, + stream{info.stream}, + framing{std::make_shared(info.stream)} { + auto it = std::ranges::find_if( + protocols_groups, [&](const StreamProtocols &protocols) { + return std::ranges::find(protocols, info.protocol) != protocols.end(); + }); + protocol_group = + it == protocols_groups.end() ? 0 : it - protocols_groups.begin(); + } + + StreamInfoClose::StreamInfoClose(StreamInfo &&info) + : StreamInfo{std::move(info)} {} + + StreamInfoClose::~StreamInfoClose() { + if (stream) { + stream->reset(); + } + } + + PeerOutOpen::PeerOutOpen(StreamInfoClose &&stream) + : stream{std::move(stream)}, writing{false} {} + + Protocol::Protocol(MainThreadPool &main_thread_pool, + std::shared_ptr host, + std::shared_ptr scheduler, + ProtocolsGroups protocols_groups, + size_t limit_in, + size_t limit_out) + : main_pool_handler_{main_thread_pool.handlerStarted()}, + host_{std::move(host)}, + own_peer_id_{host_->getId()}, + scheduler_{std::move(scheduler)}, + protocols_groups_{std::move(protocols_groups)}, + limit_in_{limit_in}, + limit_out_{limit_out} { + for (auto &protocols : protocols_groups_) { + protocols_.insert(protocols_.end(), protocols.begin(), protocols.end()); + } + } + + void Protocol::start(std::weak_ptr controller) { + REINVOKE(*main_pool_handler_, start, std::move(controller)); + controller_ = std::move(controller); + host_->setProtocolHandler( + protocols_, [WEAK_SELF](const StreamAndProtocol &info) { + WEAK_LOCK(self); + auto peer_id = info.stream->remotePeerId().value(); + self->onStream(peer_id, info, false); + }); + timer(); + } + + std::optional Protocol::peerOut(const PeerId &peer_id) { + EXPECT_THREAD(*main_pool_handler_); + if (auto peer = entry(peers_out_, peer_id)) { + if (auto *open = std::get_if(&*peer)) { + return open->stream.protocol_group; + } + } + return std::nullopt; + } + + void Protocol::peersOut(const PeersOutCb &cb) const { + EXPECT_THREAD(*main_pool_handler_); + for (auto &[peer_id, peer] : peers_out_) { + auto *open = std::get_if(&peer); + if (not open) { + continue; + } + if (not cb(peer_id, open->stream.protocol_group)) { + break; + } + } + } + + void Protocol::write(const PeerId &peer_id, + size_t protocol_group, + std::shared_ptr message) { + REINVOKE(*main_pool_handler_, + write, + peer_id, + protocol_group, + std::move(message)); + auto peer = entry(peers_out_, peer_id); + if (not peer) { + return; + } + auto *open = std::get_if(&*peer); + if (not open) { + return; + } + if (open->stream.protocol_group != protocol_group) { + return; + } + open->queue.emplace_back(std::move(message)); + write(peer_id, false); + } + + void Protocol::write(const PeerId &peer_id, std::shared_ptr message) { + if (protocols_groups_.size() != 1) { + throw std::logic_error{"write on ambigous protocol"}; + } + write(peer_id, 0, std::move(message)); + } + + void Protocol::reserve(const PeerId &peer_id, bool add) { + REINVOKE(*main_pool_handler_, reserve, peer_id, add); + if (add) { + if (not reserved_.emplace(peer_id).second) { + return; + } + } else { + if (reserved_.erase(peer_id) == 0) { + return; + } + } + } + + void Protocol::onError(const PeerId &peer_id, bool out) { + auto closed = false; + auto peer_out = entry(peers_out_, peer_id); + auto peer_out_open = + peer_out and std::holds_alternative(*peer_out); + auto peer_in = entry(peers_in_, peer_id); + if (out) { + backoff(peer_id); + if (not peer_out_open) { + return; + } + closed = not peer_in; + } else { + if (not peer_in) { + return; + } + peer_in.remove(); + closed = not peer_out_open; + } + if (closed) { + if (auto controller = controller_.lock()) { + controller->onClose(peer_id); + } + } + } + + std::chrono::milliseconds Protocol::backoffTime() { + return std::chrono::milliseconds{std::uniform_int_distribution<>{ + std::chrono::milliseconds{kBackoffMin}.count(), + std::chrono::milliseconds{kBackoffMax}.count(), + }(random_)}; + } + + void Protocol::backoff(const PeerId &peer_id) { + auto peer = entry(peers_out_, peer_id); + if (not peer) { + return; + } + peer.insert_or_assign(PeerOutBackoff{ + scheduler_->scheduleWithHandle( + [WEAK_SELF, peer_id] { + WEAK_LOCK(self); + self->onBackoff(peer_id); + }, + backoffTime()), + }); + } + + void Protocol::onBackoff(const PeerId &peer_id) { + auto peer = entry(peers_out_, peer_id); + if (not peer) { + return; + } + if (not std::holds_alternative(*peer)) { + return; + } + peer.remove(); + } + + void Protocol::open(const PeerId &peer_id) { + if (peer_id == own_peer_id_) { + return; + } + if (not peers_out_.emplace(peer_id, PeerOutOpening{}).second) { + return; + } + auto cb = [WEAK_SELF, peer_id](StreamAndProtocolOrError r) { + WEAK_LOCK(self); + if (not r) { + self->onError(peer_id, true); + return; + } + self->onStream(peer_id, r.value(), true); + }; + newStream(*host_, peer_id, protocols_, std::move(cb)); + } + + void Protocol::onStream(const PeerId &peer_id, + const StreamAndProtocol &info, + bool out) { + auto reject = [&] { info.stream->reset(); }; + auto controller = controller_.lock(); + if (not controller) { + reject(); + return; + } + if (not out) { + if (not shouldAccept(peer_id)) { + reject(); + return; + } + } + StreamInfo stream{protocols_groups_, info}; + auto cb = [WEAK_SELF, peer_id, out, stream]( + MessageReadWriterUvarint::ReadCallback r) mutable { + WEAK_LOCK(self); + if (not r) { + self->onError(peer_id, out); + return; + } + self->onHandshake(peer_id, + out, + std::move(*r.value()), + StreamInfoClose{std::move(stream)}); + }; + handshakeRaw( + stream.stream, stream.framing, controller->handshake(), std::move(cb)); + } + + void Protocol::onHandshake(const PeerId &peer_id, + bool out, + Buffer &&handshake, + StreamInfoClose &&stream) { + auto protocol_group = stream.protocol_group; + auto controller = controller_.lock(); + if (not controller) { + return; + } + if (out) { + auto peer = entry(peers_out_, peer_id); + if (not peer) { + return; + } + if (not std::holds_alternative(*peer)) { + return; + } + auto buffer = std::make_shared(1); + stream.stream->read( + *buffer, + buffer->size(), + [WEAK_SELF, peer_id, buffer](outcome::result) { + WEAK_LOCK(self); + self->onError(peer_id, true); + }); + *peer = PeerOutOpen{std::move(stream)}; + } else { + if (not shouldAccept(peer_id)) { + return; + } + } + if (not controller->onHandshake( + peer_id, protocol_group, out, std::move(handshake))) { + onError(peer_id, out); + return; + } + if (not out) { + peers_in_.emplace(peer_id, std::move(stream)); + open(peer_id); + read(peer_id); + } + } + + void Protocol::write(const PeerId &peer_id, bool writer) { + auto peer = entry(peers_out_, peer_id); + if (not peer) { + return; + } + auto *open = std::get_if(&*peer); + if (not open) { + return; + } + if (not writer and open->writing) { + return; + } + if (open->queue.empty()) { + if (writer) { + open->writing = false; + } + return; + } + open->writing = true; + auto message = std::move(open->queue.front()); + open->queue.pop_front(); + auto cb = [WEAK_SELF, peer_id](outcome::result r) { + WEAK_LOCK(self); + if (not r) { + self->onError(peer_id, true); + return; + } + self->write(peer_id, true); + }; + // MessageReadWriterUvarint copies message + open->stream.framing->write(*message, std::move(cb)); + } + + void Protocol::read(const PeerId &peer_id) { + auto stream = entry(peers_in_, peer_id); + if (not stream) { + return; + } + auto cb = [WEAK_SELF, peer_id, protocol_group{stream->protocol_group}]( + libp2p::basic::MessageReadWriter::ReadCallback r) mutable { + WEAK_LOCK(self); + if (not r) { + self->onError(peer_id, false); + return; + } + auto &message = r.value(); + self->onMessage(peer_id, + protocol_group, + // TODO(turuslan): `MessageReadWriterUvarint` reuse buffer + message ? Buffer{std::move(*message)} : Buffer{}); + }; + stream->framing->read(std::move(cb)); + } + + void Protocol::onMessage(const PeerId &peer_id, + size_t protocol, + Buffer &&message) { + auto controller = controller_.lock(); + if (not controller) { + onError(peer_id, false); + return; + } + if (not controller->onMessage(peer_id, protocol, std::move(message))) { + onError(peer_id, false); + return; + } + read(peer_id); + } + + void Protocol::timer() { + timer_ = scheduler_->scheduleWithHandle( + [WEAK_SELF] { + WEAK_LOCK(self); + self->onTimer(); + self->timer(); + }, + kTimer); + } + + void Protocol::onTimer() { + if (controller_.expired()) { + return; + } + for (auto &peer_id : reserved_) { + open(peer_id); + } + auto count = peerCount(true); + if (count >= limit_out_) { + return; + } + for (auto &conn : + host_->getNetwork().getConnectionManager().getConnections()) { + auto peer_id = conn->remotePeer().value(); + if (reserved_.contains(peer_id)) { + continue; + } + if (peers_out_.contains(peer_id)) { + continue; + } + open(conn->remotePeer().value()); + ++count; + if (count >= limit_out_) { + break; + } + } + } + + size_t Protocol::peerCount(bool out) { + size_t count = 0; + if (out) { + for (auto &p : peers_in_) { + if (reserved_.contains(p.first)) { + continue; + } + ++count; + } + } else { + for (auto &[peer_id, peer] : peers_out_) { + if (reserved_.contains(peer_id)) { + continue; + } + if (std::holds_alternative(peer)) { + continue; + } + ++count; + } + } + return count; + } + + bool Protocol::shouldAccept(const PeerId &peer_id) { + if (peers_in_.contains(peer_id)) { + return false; + } + if (reserved_.contains(peer_id)) { + return true; + } + auto peer_out = entry(peers_out_, peer_id); + if (peer_out and not std::holds_alternative(*peer_out)) { + return true; + } + return peerCount(false) < limit_in_; + } + + Factory::Factory(std::shared_ptr main_thread_pool, + std::shared_ptr host, + std::shared_ptr scheduler) + : main_thread_pool_{std::move(main_thread_pool)}, + host_{std::move(host)}, + scheduler_{std::move(scheduler)} {} + + std::shared_ptr Factory::make(ProtocolsGroups protocols_groups, + size_t limit_in, + size_t limit_out) const { + return std::make_shared(*main_thread_pool_, + host_, + scheduler_, + std::move(protocols_groups), + limit_in, + limit_out); + } +} // namespace kagome::network::notifications diff --git a/core/network/notifications/protocol.hpp b/core/network/notifications/protocol.hpp new file mode 100644 index 0000000000..a8c6859a13 --- /dev/null +++ b/core/network/notifications/protocol.hpp @@ -0,0 +1,237 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common/buffer.hpp" + +namespace libp2p { + struct Host; +} // namespace libp2p + +namespace libp2p::basic { + class Scheduler; + class MessageReadWriterUvarint; +} // namespace libp2p::basic + +namespace kagome { + class PoolHandler; +} // namespace kagome + +namespace kagome::common { + class MainThreadPool; +} // namespace kagome::common + +namespace kagome::network::notifications { + using common::MainThreadPool; + using libp2p::Cancel; + using libp2p::Host; + using libp2p::PeerId; + using libp2p::StreamAndProtocol; + using libp2p::StreamAndProtocolOrError; + using libp2p::StreamProtocols; + using libp2p::basic::MessageReadWriterUvarint; + using libp2p::basic::Scheduler; + using libp2p::connection::Stream; + using ProtocolsGroups = std::vector; + + /** + * Contains stream, framing, used protocol. + */ + struct StreamInfo { + StreamInfo(const ProtocolsGroups &protocols_groups, + const StreamAndProtocol &info); + + size_t protocol_group; + std::shared_ptr stream; + std::shared_ptr framing; + }; + + /** + * Closes stream when destroyed. + */ + struct StreamInfoClose : StreamInfo { + StreamInfoClose(StreamInfo &&info); + StreamInfoClose(StreamInfoClose &&) noexcept = default; + StreamInfoClose &operator=(StreamInfoClose &&) = default; + StreamInfoClose(const StreamInfoClose &) noexcept = delete; + StreamInfoClose &operator=(const StreamInfoClose &) = delete; + ~StreamInfoClose(); + }; + + /** + * State for opening outgoing stream. + */ + struct PeerOutOpening {}; + /** + * State for open outgoing stream. + */ + struct PeerOutOpen { + PeerOutOpen(StreamInfoClose &&stream); + + StreamInfoClose stream; + bool writing; + std::deque> queue; + }; + /** + * State for backed off outgoing stream. + */ + struct PeerOutBackoff { + Cancel timer; + }; + /** + * State for outgoing stream. + */ + using PeerOut = std::variant; + + /** + * Provides handshake and event callbacks. + */ + struct Controller { + virtual ~Controller() = default; + /** + * @returns handshake to send + */ + virtual Buffer handshake() = 0; + /** + * Stream with peer was opened/accepted. + * @returns false to close stream + */ + virtual bool onHandshake(const PeerId &peer_id, + size_t protocol_group, + bool out, + Buffer &&handshake) = 0; + /** + * Message was received from peer. + * @returns false to close stream + */ + virtual bool onMessage(const PeerId &peer_id, + size_t protocol_group, + Buffer &&message) = 0; + /** + * Both incoming/outgoing streams with peer were closed. + */ + virtual void onClose(const PeerId &peer_id) = 0; + }; + + /** + * Implements notification protocol behavior. + */ + class Protocol : public std::enable_shared_from_this { + public: + Protocol(MainThreadPool &main_thread_pool, + std::shared_ptr host, + std::shared_ptr scheduler, + ProtocolsGroups protocols_groups, + size_t limit_in, + size_t limit_out); + + /** + * Part of constructor: + * 1. `weak_from_this()` doesn't work in constructor. + * 2. possible circular reference with controller. + */ + void start(std::weak_ptr controller); + using PeersOutCb = + std::function; + /** + * Get protocol used by peer. + */ + std::optional peerOut(const PeerId &peer_id); + /** + * Visit peers and protocols they use. + * Used for broadcast. + */ + void peersOut(const PeersOutCb &cb) const; + /** + * Write message with specified protocol to peer. + * Message is ignored if peer protocol doesn't match. + */ + void write(const PeerId &peer_id, + size_t protocol_group, + std::shared_ptr message); + /** + * Write message to peer. + * Expects single protocol. + */ + void write(const PeerId &peer_id, std::shared_ptr message); + /** + * Add/remove peer to reserved set. + * Reserved peers are not affected by limits. + * Streams are automatically opened to reserved peers. + */ + void reserve(const PeerId &peer_id, bool add); + + private: + void onError(const PeerId &peer_id, bool out); + std::chrono::milliseconds backoffTime(); + void backoff(const PeerId &peer_id); + void onBackoff(const PeerId &peer_id); + void open(const PeerId &peer_id); + void onStream(const PeerId &peer_id, + const StreamAndProtocol &info, + bool out); + void onHandshake(const PeerId &peer_id, + bool out, + Buffer &&handshake, + StreamInfoClose &&stream); + void write(const PeerId &peer_id, bool writer); + void read(const PeerId &peer_id); + void onMessage(const PeerId &peer_id, + size_t protocol_group, + Buffer &&message); + void timer(); + void onTimer(); + size_t peerCount(bool out); + bool shouldAccept(const PeerId &peer_id); + + std::shared_ptr main_pool_handler_; + std::shared_ptr host_; + PeerId own_peer_id_; + std::shared_ptr scheduler_; + ProtocolsGroups protocols_groups_; + size_t limit_in_; + size_t limit_out_; + StreamProtocols protocols_; + std::weak_ptr controller_; + Cancel timer_; + std::default_random_engine random_; + std::unordered_map peers_out_; + std::unordered_map peers_in_; + std::unordered_set reserved_; + }; + + /** + * Contains common constructor parameters for `Protocol` to avoid specifying + * them explicitly. + */ + class Factory { + public: + Factory(std::shared_ptr main_thread_pool, + std::shared_ptr host, + std::shared_ptr scheduler); + /** + * Make protocol with specified protocols and limits. + */ + std::shared_ptr make(ProtocolsGroups protocols_groups, + size_t limit_in, + size_t limit_out) const; + + private: + std::shared_ptr main_thread_pool_; + std::shared_ptr host_; + std::shared_ptr scheduler_; + }; +} // namespace kagome::network::notifications diff --git a/core/network/peer_manager.hpp b/core/network/peer_manager.hpp index 524810c053..690e4eda7b 100644 --- a/core/network/peer_manager.hpp +++ b/core/network/peer_manager.hpp @@ -24,9 +24,6 @@ #include "utils/non_copyable.hpp" namespace kagome::network { - - struct StreamEngine; - /** * Manage active peers: * - peer discovery (internally) @@ -51,21 +48,6 @@ namespace kagome::network { */ virtual void connectToPeer(const PeerInfo &peer_info) = 0; - /** - * Reserves stream slots of needed protocols for peer by {@param peer_id} - */ - virtual void reserveStreams(const PeerId &peer_id) const = 0; - - /** - * Reserves streams needed to update our status. - */ - virtual void reserveStatusStreams(const PeerId &peer_id) const = 0; - - /** - * Return stream engine object. - */ - virtual std::shared_ptr getStreamEngine() = 0; - /** * Keeps peer with {@param peer_id} alive */ diff --git a/core/network/peer_state.hpp b/core/network/peer_state.hpp index fdb0782dfc..109d478358 100644 --- a/core/network/peer_state.hpp +++ b/core/network/peer_state.hpp @@ -25,7 +25,6 @@ namespace kagome::network { - constexpr size_t kPeerStateMaxKnownBlocks = 1024; constexpr size_t kPeerStateMaxKnownGrandpaMessages = 8192; using RoundNumber = consensus::grandpa::RoundNumber; using VoterSetId = consensus::grandpa::VoterSetId; @@ -53,10 +52,6 @@ namespace kagome::network { std::optional round_number = std::nullopt; std::optional set_id = std::nullopt; BlockNumber last_finalized = 0; - LruSet known_blocks{kPeerStateMaxKnownBlocks}; - LruSet known_grandpa_messages{ - kPeerStateMaxKnownGrandpaMessages, - }; /// @brief parachain peer state std::optional collator_state = std::nullopt; diff --git a/core/network/peer_view.hpp b/core/network/peer_view.hpp index 2cbc91aae4..60d768321b 100644 --- a/core/network/peer_view.hpp +++ b/core/network/peer_view.hpp @@ -51,15 +51,15 @@ namespace kagome::network { using PeerId = libp2p::peer::PeerId; - using MyViewSubscriptionEngine = - subscription::SubscriptionEngine; + using MyViewSubscriptionEngine = subscription:: + SubscriptionEngine; using MyViewSubscriptionEnginePtr = std::shared_ptr; using MyViewSubscriber = MyViewSubscriptionEngine::SubscriberType; using MyViewSubscriberPtr = std::shared_ptr; using PeerViewSubscriptionEngine = subscription:: - SubscriptionEngine; + SubscriptionEngine; using PeerViewSubscriptionEnginePtr = std::shared_ptr; using PeerViewSubscriber = PeerViewSubscriptionEngine::SubscriberType; @@ -83,19 +83,21 @@ namespace kagome::network { void removePeer(const PeerId &peer_id); void updateRemoteView(const PeerId &peer_id, network::View &&view); - std::optional> getMyView() const; + auto &getMyView() const { + return my_view_; + } private: - void updateMyView(network::ExView &&view); + void updateMyView(const primitives::BlockHeader &header); primitives::events::ChainSub chain_sub_; + LazySPtr block_tree_; MyViewSubscriptionEnginePtr my_view_update_observable_; PeerViewSubscriptionEnginePtr remote_view_update_observable_; - std::optional my_view_; + View my_view_; SafeObject> remote_view_; - LazySPtr block_tree_; }; } // namespace kagome::network diff --git a/core/network/protocols/beefy_protocol.hpp b/core/network/protocols/beefy_protocol.hpp index 56ce4905c4..8fff1384a7 100644 --- a/core/network/protocols/beefy_protocol.hpp +++ b/core/network/protocols/beefy_protocol.hpp @@ -6,13 +6,14 @@ #pragma once -#include "network/protocol_base.hpp" +#include #include "consensus/beefy/types.hpp" namespace kagome::network { - class BeefyProtocol : public virtual ProtocolBase { + class BeefyProtocol { public: + virtual ~BeefyProtocol() = default; virtual void broadcast( std::shared_ptr message) = 0; }; diff --git a/core/network/router.hpp b/core/network/router.hpp index 56a8ee9ff0..613c16c967 100644 --- a/core/network/router.hpp +++ b/core/network/router.hpp @@ -29,8 +29,6 @@ namespace kagome::network { class GrandpaProtocol; class SendDisputeProtocol; class BeefyProtocol; - class CollationProtocolVStaging; - class ValidationProtocolVStaging; class FetchAttestedCandidateProtocol; using Ping = libp2p::protocol::Ping; } // namespace kagome::network @@ -48,12 +46,8 @@ namespace kagome::network { virtual std::shared_ptr getBlockAnnounceProtocol() const = 0; virtual std::shared_ptr getCollationProtocol() const = 0; - virtual std::shared_ptr - getCollationProtocolVStaging() const = 0; virtual std::shared_ptr getValidationProtocol() const = 0; - virtual std::shared_ptr - getValidationProtocolVStaging() const = 0; virtual std::shared_ptr getReqCollationProtocol() const = 0; virtual std::shared_ptr getReqPovProtocol() const = 0; diff --git a/core/network/transactions_transmitter.hpp b/core/network/transactions_transmitter.hpp index c8fbc33cb6..db6d109ebf 100644 --- a/core/network/transactions_transmitter.hpp +++ b/core/network/transactions_transmitter.hpp @@ -19,10 +19,8 @@ namespace kagome::network { virtual ~TransactionsTransmitter() = default; /** - * Send Transactions message - * @param txs - list of transaction to be sent + * Send Transaction message */ - virtual void propagateTransactions( - std::span txs) = 0; + virtual void propagateTransaction(primitives::Transaction tx) = 0; }; } // namespace kagome::network diff --git a/core/network/types/block_announce_handshake.hpp b/core/network/types/block_announce_handshake.hpp index 102973d520..a7acbdc40c 100644 --- a/core/network/types/block_announce_handshake.hpp +++ b/core/network/types/block_announce_handshake.hpp @@ -6,13 +6,8 @@ #pragma once -#include -#include -#include - #include "network/types/roles.hpp" #include "primitives/common.hpp" -#include "scale/scale.hpp" namespace kagome::network { diff --git a/core/network/types/collator_messages.hpp b/core/network/types/collator_messages.hpp index 792457ea8b..0687abd3cd 100644 --- a/core/network/types/collator_messages.hpp +++ b/core/network/types/collator_messages.hpp @@ -409,7 +409,7 @@ namespace kagome::network { StatementDistributionMessage, /// statement distribution message ApprovalDistributionMessage /// approval distribution message >; - using CollationProtocolMessage = boost::variant; + using CollationMessage0 = boost::variant; template struct AllowerTypeChecker { diff --git a/core/network/types/collator_messages_vstaging.hpp b/core/network/types/collator_messages_vstaging.hpp index ecfea5cae6..48ea8582a2 100644 --- a/core/network/types/collator_messages_vstaging.hpp +++ b/core/network/types/collator_messages_vstaging.hpp @@ -89,7 +89,7 @@ namespace kagome::network::vstaging { Dummy, CollatorProtocolMessageCollationSeconded>; - using CollatorProtocolMessage = boost::variant; + using CollationMessage0 = boost::variant; struct SecondedCandidateHash { SCALE_TIE(1); @@ -476,22 +476,21 @@ namespace kagome::network { template using WireMessage = boost::variant< Dummy, /// not used - std::enable_if_t< - AllowerTypeChecker::allowed, - T>, /// protocol message - ViewUpdate /// view update message + std::enable_if_t::allowed, + T>, /// protocol message + ViewUpdate /// view update message >; template using Versioned = boost::variant; using VersionedCollatorProtocolMessage = - Versioned; + Versioned; using VersionedValidatorProtocolMessage = Versioned; diff --git a/core/network/validation_observer.hpp b/core/network/validation_observer.hpp index 51d3593149..52ea10db58 100644 --- a/core/network/validation_observer.hpp +++ b/core/network/validation_observer.hpp @@ -19,11 +19,6 @@ namespace kagome::network { public: virtual ~ValidationObserver() = default; - /// Handle incoming validation stream. - virtual void onIncomingValidationStream( - const libp2p::peer::PeerId &peer_id, - network::CollationVersion version) = 0; - /// Handle incoming collation message. virtual void onIncomingMessage( const libp2p::peer::PeerId &peer_id, diff --git a/core/parachain/approval/approval_distribution.cpp b/core/parachain/approval/approval_distribution.cpp index 4c74fb57d6..7522517cf1 100644 --- a/core/parachain/approval/approval_distribution.cpp +++ b/core/parachain/approval/approval_distribution.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -20,8 +21,7 @@ #include "crypto/hasher.hpp" #include "crypto/key_store.hpp" #include "crypto/sr25519_provider.hpp" -#include "network/impl/protocols/parachain_protocols.hpp" -#include "network/impl/stream_engine.hpp" +#include "network/impl/protocols/parachain.hpp" #include "network/peer_manager.hpp" #include "network/router.hpp" #include "parachain/approval/approval.hpp" @@ -29,8 +29,11 @@ #include "parachain/approval/approval_distribution_error.hpp" #include "parachain/approval/approval_thread_pool.hpp" #include "parachain/approval/state.hpp" +#include "parachain/pvf/pvf.hpp" +#include "parachain/validator/parachain_processor.hpp" #include "primitives/math.hpp" #include "runtime/runtime_api/parachain_host_types.hpp" +#include "utils/map.hpp" #include "utils/pool_handler_ready_make.hpp" #include "utils/weak_macro.hpp" @@ -585,7 +588,7 @@ namespace kagome::parachain { common::MainThreadPool &main_thread_pool, LazySPtr dispute_coordinator) : approval_thread_handler_{poolHandlerReadyMake( - this, app_state_manager, approval_thread_pool, logger_)}, + this, app_state_manager, approval_thread_pool, logger_)}, worker_pool_handler_{worker_thread_pool.handler(*app_state_manager)}, parachain_host_(std::move(parachain_host)), slots_util_(slots_util), @@ -634,35 +637,28 @@ namespace kagome::parachain { } bool ApprovalDistribution::tryStart() { - my_view_sub_ = std::make_shared( - peer_view_->getMyViewObservable(), false); - primitives::events::subscribe( - *my_view_sub_, + my_view_sub_ = primitives::events::subscribe( + peer_view_->getMyViewObservable(), network::PeerView::EventType::kViewUpdated, - [wptr{weak_from_this()}](const network::ExView &event) { - if (auto self = wptr.lock()) { - self->on_active_leaves_update(event); - } + [WEAK_SELF](const network::ExView &event) { + WEAK_LOCK(self); + self->on_active_leaves_update(event); }); - remote_view_sub_ = std::make_shared( - peer_view_->getRemoteViewObservable(), false); - primitives::events::subscribe( - *remote_view_sub_, + remote_view_sub_ = primitives::events::subscribe( + peer_view_->getRemoteViewObservable(), network::PeerView::EventType::kViewUpdated, - [wptr{weak_from_this()}](const libp2p::peer::PeerId &peer_id, - const network::View &view) { - if (auto self = wptr.lock()) { - self->store_remote_view(peer_id, view); - } + [WEAK_SELF](const libp2p::peer::PeerId &peer_id, + const network::View &view) { + WEAK_LOCK(self); + self->store_remote_view(peer_id, view); }); chain_sub_.onDeactivate( - [wptr{weak_from_this()}]( + [WEAK_SELF]( const primitives::events::RemoveAfterFinalizationParams &event) { - if (auto self = wptr.lock()) { - self->clearCaches(event); - } + WEAK_LOCK(self); + self->clearCaches(event); }); /// TODO(iceseer): clear `known_by` when peer disconnected @@ -2587,20 +2583,14 @@ namespace kagome::parachain { "Distributing assignment on candidate (block hash={})", indirect_cert.block_hash); - auto se = pm_->getStreamEngine(); - BOOST_ASSERT(se); - - se->broadcast( - router_->getValidationProtocolVStaging(), - std::make_shared< - network::WireMessage>( - network::vstaging::ApprovalDistributionMessage{ - network::vstaging::Assignments{ - .assignments = {network::vstaging::Assignment{ - .indirect_assignment_cert = indirect_cert, - .candidate_bitfield = candidate_indices, - }}}}), - [&](const libp2p::peer::PeerId &p) { return peers.contains(p); }); + router_->getValidationProtocol()->write( + peers, + network::vstaging::Assignments{ + .assignments = {network::vstaging::Assignment{ + .indirect_assignment_cert = indirect_cert, + .candidate_bitfield = candidate_indices, + }}, + }); } void ApprovalDistribution::send_assignments_batched( @@ -2611,9 +2601,6 @@ namespace kagome::parachain { std::move(assignments), peer_id); - auto se = pm_->getStreamEngine(); - BOOST_ASSERT(se); // kMaxAssignmentBatchSize - /** TODO(iceseer): optimize std::shared_ptr> pack = std::make_shared< @@ -2633,16 +2620,11 @@ namespace kagome::parachain { auto end = (assignments.size() > kMaxAssignmentBatchSize) ? assignments.begin() + kMaxAssignmentBatchSize : assignments.end(); - - auto msg = std::make_shared< - network::WireMessage>( - network::vstaging::ApprovalDistributionMessage{ - network::vstaging::Assignments{ - .assignments = - std::vector(begin, end), - }}); - - se->send(peer_id, router_->getValidationProtocolVStaging(), msg); + router_->getValidationProtocol()->write( + peer_id, + network::vstaging::Assignments{ + .assignments = std::vector(begin, end), + }); assignments.erase(begin, end); } } @@ -2655,9 +2637,6 @@ namespace kagome::parachain { std::move(approvals), peer_id); - auto se = pm_->getStreamEngine(); - BOOST_ASSERT(se); // kMaxApprovalBatchSize - /** TODO(iceseer): optimize std::shared_ptr> pack = std::make_shared< @@ -2682,17 +2661,11 @@ namespace kagome::parachain { auto end = (approvals.size() > kMaxApprovalBatchSize) ? approvals.begin() + kMaxApprovalBatchSize : approvals.end(); - - auto msg = std::make_shared< - network::WireMessage>( - network::vstaging::ApprovalDistributionMessage{ - network::vstaging::Approvals{ - .approvals = - std::vector(begin, - end), - }}); - - se->send(peer_id, router_->getValidationProtocolVStaging(), msg); + router_->getValidationProtocol()->write( + peer_id, + network::vstaging::Approvals{ + .approvals = std::vector(begin, end), + }); approvals.erase(begin, end); } } @@ -2708,18 +2681,10 @@ namespace kagome::parachain { vote.payload.payload.block_hash, peers.size()); - auto se = pm_->getStreamEngine(); - BOOST_ASSERT(se); - - se->broadcast( - router_->getValidationProtocolVStaging(), - std::make_shared< - network::WireMessage>( - network::vstaging::ApprovalDistributionMessage{ - network::vstaging::Approvals{ - .approvals = {vote}, - }}), - [&](const libp2p::peer::PeerId &p) { return peers.contains(p); }); + router_->getValidationProtocol()->write(peers, + network::vstaging::Approvals{ + .approvals = {vote}, + }); } void ApprovalDistribution::issue_approval(const CandidateHash &candidate_hash, @@ -2933,10 +2898,9 @@ namespace kagome::parachain { }; return approval::min_or_some( e.next_no_show, - (e.last_assignment_tick - ? filter(*e.last_assignment_tick + kApprovalDelay, - tick_now) - : std::optional{})); + (e.last_assignment_tick ? filter( + *e.last_assignment_tick + kApprovalDelay, tick_now) + : std::optional{})); }, [&](const approval::PendingRequiredTranche &e) { std::optional next_announced{}; diff --git a/core/parachain/approval/approval_distribution.hpp b/core/parachain/approval/approval_distribution.hpp index c03ca5581f..fab2b29bc1 100644 --- a/core/parachain/approval/approval_distribution.hpp +++ b/core/parachain/approval/approval_distribution.hpp @@ -26,6 +26,7 @@ #include "crypto/type_hasher.hpp" #include "dispute_coordinator/dispute_coordinator.hpp" #include "injector/lazy.hpp" +#include "metrics/metrics.hpp" #include "network/peer_view.hpp" #include "network/types/collator_messages_vstaging.hpp" #include "parachain/approval/approved_ancestor.hpp" @@ -33,7 +34,6 @@ #include "parachain/approval/store.hpp" #include "parachain/availability/recovery/recovery.hpp" #include "parachain/backing/grid.hpp" -#include "parachain/validator/parachain_processor.hpp" #include "runtime/runtime_api/parachain_host.hpp" #include "runtime/runtime_api/parachain_host_types.hpp" #include "utils/safe_object.hpp" @@ -48,13 +48,20 @@ namespace kagome::common { class WorkerThreadPool; } // namespace kagome::common +namespace kagome::network { + class PeerManager; + class Router; +} // namespace kagome::network + namespace kagome::consensus::babe { class BabeConfigRepository; } namespace kagome::parachain { class ApprovalThreadPool; -} + class ParachainProcessorImpl; + class Pvf; +} // namespace kagome::parachain namespace kagome::parachain { using DistributeAssignment = network::Assignment; @@ -72,9 +79,10 @@ namespace kagome::parachain { * approvals for valid candidates, respectively disputes for invalid * candidates. */ - struct ApprovalDistribution final + class ApprovalDistribution final : public std::enable_shared_from_this, public IApprovedAncestor { + public: struct OurAssignment { SCALE_TIE(4); approval::AssignmentCertV2 cert; @@ -220,7 +228,7 @@ namespace kagome::parachain { SessionIndex session; // Assignments are based on blocks, so we need to track assignments // separately based on the block we are looking at. - std::unordered_map block_assignments; + std::unordered_map block_assignments; scale::BitVec approvals; CandidateEntry(const HashedCandidateReceipt &hashed_receipt, @@ -233,9 +241,8 @@ namespace kagome::parachain { CandidateEntry(const network::CandidateReceipt &receipt, SessionIndex session_index, size_t approvals_size) - : CandidateEntry(HashedCandidateReceipt{receipt}, - session_index, - approvals_size) {} + : CandidateEntry( + HashedCandidateReceipt{receipt}, session_index, approvals_size) {} std::optional> approval_entry( const network::RelayHash &relay_hash) { @@ -825,12 +832,12 @@ namespace kagome::parachain { const network::View &view); auto &storedBlocks() { - return as>>(store_); + return as>>( + store_); } auto &storedCandidateEntries() { - return as>(store_); + return as>(store_); } auto &storedBlockEntries() { @@ -893,7 +900,7 @@ namespace kagome::parachain { using ScheduledCandidateTimer = std::unordered_map< CandidateHash, std::vector>>; - std::unordered_map + std::unordered_map active_tranches_; struct ApprovalCache { diff --git a/core/parachain/validator/backed_candidates_source.hpp b/core/parachain/validator/backed_candidates_source.hpp new file mode 100644 index 0000000000..9655c82350 --- /dev/null +++ b/core/parachain/validator/backed_candidates_source.hpp @@ -0,0 +1,21 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "parachain/types.hpp" + +namespace kagome::network { + struct BackedCandidate; +} // namespace kagome::network + +namespace kagome::parachain { + struct BackedCandidatesSource { + virtual ~BackedCandidatesSource() = default; + virtual std::vector getBackedCandidates( + const RelayHash &relay_parent) = 0; + }; +} // namespace kagome::parachain diff --git a/core/parachain/validator/impl/parachain_observer_impl.cpp b/core/parachain/validator/impl/parachain_observer_impl.cpp index 1f3e281118..e28e566543 100644 --- a/core/parachain/validator/impl/parachain_observer_impl.cpp +++ b/core/parachain/validator/impl/parachain_observer_impl.cpp @@ -41,9 +41,9 @@ namespace kagome::parachain { network::VersionedCollatorProtocolMessage &&msg) { visit_in_place( std::move(msg), - [&](kagome::network::CollationMessage &&collation_msg) { + [&](kagome::network::CollationMessage0 &&msg0) { visit_in_place( - std::move(collation_msg), + std::move(boost::get(msg0)), [&](network::CollatorDeclaration &&collation_decl) { onDeclare(peer_id, collation_decl.collator_id, @@ -60,8 +60,7 @@ namespace kagome::parachain { SL_WARN(logger_, "Unexpected V1 collation message from."); }); }, - [&](kagome::network::vstaging::CollatorProtocolMessage - &&collation_msg) { + [&](kagome::network::vstaging::CollationMessage0 &&collation_msg) { if (auto m = if_type(collation_msg)) { visit_in_place( @@ -97,16 +96,6 @@ namespace kagome::parachain { }); } - void ParachainObserverImpl::onIncomingCollationStream( - const libp2p::peer::PeerId &peer_id, network::CollationVersion version) { - processor_->onIncomingCollationStream(peer_id, version); - } - - void ParachainObserverImpl::onIncomingValidationStream( - const libp2p::peer::PeerId &peer_id, network::CollationVersion version) { - processor_->onIncomingValidationStream(peer_id, version); - } - void ParachainObserverImpl::onIncomingMessage( const libp2p::peer::PeerId &peer_id, network::VersionedValidatorProtocolMessage &&message) { diff --git a/core/parachain/validator/impl/parachain_observer_impl.hpp b/core/parachain/validator/impl/parachain_observer_impl.hpp index 23fc89b1ed..b708921905 100644 --- a/core/parachain/validator/impl/parachain_observer_impl.hpp +++ b/core/parachain/validator/impl/parachain_observer_impl.hpp @@ -21,8 +21,8 @@ namespace kagome::crypto { } namespace kagome::parachain { - struct ParachainProcessorImpl; - struct ApprovalDistribution; + class ParachainProcessorImpl; + class ApprovalDistribution; } // namespace kagome::parachain namespace kagome::parachain { @@ -40,15 +40,11 @@ namespace kagome::parachain { void onIncomingMessage( const libp2p::peer::PeerId &peer_id, network::VersionedCollatorProtocolMessage &&msg) override; - void onIncomingCollationStream(const libp2p::peer::PeerId &peer_id, - network::CollationVersion version) override; /// validation protocol observer void onIncomingMessage(const libp2p::peer::PeerId &peer_id, network::VersionedValidatorProtocolMessage &&validation_message) override; - void onIncomingValidationStream(const libp2p::peer::PeerId &peer_id, - network::CollationVersion version) override; /// fetch collation protocol observer outcome::result OnCollationRequest( diff --git a/core/parachain/validator/impl/parachain_processor.cpp b/core/parachain/validator/impl/parachain_processor.cpp index 53dc2bb19b..f272db5419 100644 --- a/core/parachain/validator/impl/parachain_processor.cpp +++ b/core/parachain/validator/impl/parachain_processor.cpp @@ -21,10 +21,9 @@ #include "dispute_coordinator/impl/runtime_info.hpp" #include "network/common.hpp" #include "network/impl/protocols/fetch_attested_candidate.hpp" -#include "network/impl/protocols/parachain_protocols.hpp" +#include "network/impl/protocols/parachain.hpp" #include "network/impl/protocols/protocol_req_collation.hpp" #include "network/impl/protocols/protocol_req_pov.hpp" -#include "network/impl/stream_engine.hpp" #include "network/peer_manager.hpp" #include "network/router.hpp" #include "parachain/availability/chunks.hpp" @@ -130,7 +129,6 @@ namespace { } namespace kagome::parachain { - constexpr size_t kMinGossipPeers = 25; ParachainProcessorImpl::ParachainProcessorImpl( std::shared_ptr pm, @@ -227,14 +225,10 @@ namespace kagome::parachain { const network::SignedBitfield &bitfield) { REINVOKE(*main_pool_handler_, OnBroadcastBitfields, relay_parent, bitfield); SL_TRACE(logger_, "Distribute bitfield on {}", relay_parent); - - send_to_validators_group( - relay_parent, - {network::VersionedValidatorProtocolMessage{ - network::vstaging::ValidatorProtocolMessage{ - network::vstaging::BitfieldDistributionMessage{ - network::vstaging::BitfieldDistribution{relay_parent, - bitfield}}}}}); + router_->getValidationProtocol()->write(network::BitfieldDistribution{ + .relay_parent = relay_parent, + .data = bitfield, + }); } /** @@ -257,42 +251,18 @@ namespace kagome::parachain { // Subscribe to the BABE status observable sync_state_observer_ = - std::make_shared( - sync_state_observable_, false); - sync_state_observer_->setCallback( - [wself{weak_from_this()}, was_synchronized = false]( - auto /*set_id*/, - bool &synchronized, - auto /*event_type*/, - const primitives::events::SyncStateEventParams &event) mutable { - TRY_GET_OR_RET(self, wself.lock()); - - if (event == consensus::SyncState::SYNCHRONIZED) { - if (not was_synchronized) { - self->bitfield_signer_->start(); - self->pvf_precheck_->start(); - was_synchronized = true; - } - } - if (was_synchronized) { - if (!synchronized) { - synchronized = true; - TRY_GET_OR_RET(my_view, self->peer_view_->getMyView()); - SL_TRACE(self->logger_, - "Broadcast my view because synchronized."); - self->broadcastView(my_view->get().view); - } - } + primitives::events::onSync(sync_state_observable_, [WEAK_SELF] { + WEAK_LOCK(self); + self->synchronized_ = true; + self->bitfield_signer_->start(); + self->pvf_precheck_->start(); }); - sync_state_observer_->subscribe( - sync_state_observer_->generateSubscriptionSetId(), - primitives::events::SyncStateEventType::kSyncState); // Subscribe to the chain events engine chain_sub_.onDeactivate( - [wptr{weak_from_this()}]( + [WEAK_SELF]( const primitives::events::RemoveAfterFinalizationParams &event) { - TRY_GET_OR_RET(self, wptr.lock()); + WEAK_LOCK(self); self->onDeactivateBlocks(event); }); @@ -301,13 +271,11 @@ namespace kagome::parachain { // It updates the active leaves, checks if parachains can be processed, // creates a new backing task for the new head, and broadcasts the updated // view. - my_view_sub_ = std::make_shared( - peer_view_->getMyViewObservable(), false); - primitives::events::subscribe( - *my_view_sub_, + my_view_sub_ = primitives::events::subscribe( + peer_view_->getMyViewObservable(), network::PeerView::EventType::kViewUpdated, - [wptr{weak_from_this()}](const network::ExView &event) { - TRY_GET_OR_RET(self, wptr.lock()); + [WEAK_SELF](const network::ExView &event) { + WEAK_LOCK(self); self->onViewUpdated(event); }); @@ -344,7 +312,6 @@ namespace kagome::parachain { relay_parent, event.view.finalized_number_, event.view.heads_.size()); - broadcastView(event.view); handle_active_leaves_update_for_validator(event, std::move(pruned)); } @@ -435,67 +402,6 @@ namespace kagome::parachain { } } - void ParachainProcessorImpl::broadcastViewExcept( - const libp2p::peer::PeerId &peer_id, const network::View &view) const { - auto msg = std::make_shared< - network::WireMessage>( - network::ViewUpdate{.view = view}); - pm_->getStreamEngine()->broadcast( - router_->getValidationProtocolVStaging(), - msg, - [&](const libp2p::peer::PeerId &p) { return peer_id != p; }); - } - - void ParachainProcessorImpl::broadcastViewToGroup( - const primitives::BlockHash &relay_parent, const network::View &view) { - std::deque group; - if (auto r = runtime_info_->get_session_info(relay_parent)) { - auto &[session, info] = r.value(); - if (info.our_group) { - for (auto &i : session.validator_groups[*info.our_group]) { - if (auto peer = query_audi_->get(session.discovery_keys[i])) { - group.emplace_back(peer->id); - } - } - } - } - - auto protocol = [&]() -> std::shared_ptr { - return router_->getValidationProtocolVStaging(); - }(); - - auto make_send = [&]( - const Msg &msg, - const std::shared_ptr - &protocol) { - auto se = pm_->getStreamEngine(); - auto message = std::make_shared< - network::WireMessage>( - msg); - SL_TRACE( - logger_, - "Broadcasting view update to group.(relay_parent={}, group_size={})", - relay_parent, - group.size()); - - for (const auto &peer : group) { - SL_TRACE(logger_, "Send to peer from group. (peer={})", peer); - se->send(peer, protocol, message); - } - }; - - make_send(network::vstaging::ViewUpdate{view}, - router_->getValidationProtocolVStaging()); - } - - void ParachainProcessorImpl::broadcastView(const network::View &view) const { - auto msg = std::make_shared< - network::WireMessage>( - network::ViewUpdate{.view = view}); - pm_->getStreamEngine()->broadcast(router_->getCollationProtocolVStaging(), - msg); - } - outcome::result> ParachainProcessorImpl::isParachainValidator( const primitives::BlockHash &relay_parent) const { @@ -506,34 +412,12 @@ namespace kagome::parachain { if (!isValidatingNode()) { return Error::NOT_A_VALIDATOR; } - if (!sync_state_observer_->get()) { + if (not synchronized_) { return Error::NOT_SYNCHRONIZED; } return outcome::success(); } - void ParachainProcessorImpl::spawn_and_update_peer( - std::unordered_set &cache, - const primitives::AuthorityDiscoveryId &id) { - if (cache.contains(id)) { - return; - } - - cache.insert(id); - if (auto peer = query_audi_->get(id)) { - tryOpenOutgoingValidationStream( - peer->id, - network::CollationVersion::VStaging, - [WEAK_SELF, peer_id{peer->id}]() { - WEAK_LOCK(self); - self->sendMyView(peer_id, - self->router_->getValidationProtocolVStaging()); - }); - } else { - SL_TRACE(logger_, "No audi for {}.", id); - } - } - outcome::result ParachainProcessorImpl::getBabeRandomness(const RelayHash &relay_parent) { OUTCOME_TRY(block_header, block_tree_->getBlockHeader(relay_parent)); @@ -563,11 +447,8 @@ namespace kagome::parachain { * assignment, validator index, required collator, and table context. */ bool is_parachain_validator = false; - ::libp2p::common::FinalAction metric_updater( - [wptr{weak_from_this()}, &is_parachain_validator] { - TRY_GET_OR_RET(self, wptr.lock()); - self->metric_is_parachain_validator_->set(is_parachain_validator); - }); + ::libp2p::common::FinalAction metric_updater{ + [&] { metric_is_parachain_validator_->set(is_parachain_validator); }}; OUTCOME_TRY(validators, parachain_host_->validators(relay_parent)); OUTCOME_TRY(groups, parachain_host_->validator_groups(relay_parent)); OUTCOME_TRY(cores, parachain_host_->availability_cores(relay_parent)); @@ -1142,87 +1023,6 @@ namespace kagome::parachain { bitfield_store_->putBitfield(bd->relay_parent, bd->data); } - void ParachainProcessorImpl::send_to_validators_group( - const RelayHash &relay_parent, - const std::deque &messages) { - BOOST_ASSERT(main_pool_handler_->isInCurrentThread()); - - auto se = pm_->getStreamEngine(); - std::unordered_set group_set; - if (auto r = runtime_info_->get_session_info(relay_parent)) { - auto &[session, info] = r.value(); - if (info.our_group) { - for (auto &i : session.validator_groups[*info.our_group]) { - if (auto peer = query_audi_->get(session.discovery_keys[i])) { - group_set.emplace(peer->id); - } - } - } - } - - std::deque group, any; - for (const auto &p : group_set) { - group.emplace_back(p); - } - - auto protocol = [&]() -> std::shared_ptr { - return router_->getValidationProtocolVStaging(); - }(); - - se->forEachPeer(protocol, [&](const network::PeerId &peer) { - if (not group_set.contains(peer)) { - any.emplace_back(peer); - } - }); - auto lucky = kMinGossipPeers - std::min(group.size(), kMinGossipPeers); - if (lucky != 0) { - std::shuffle(any.begin(), any.end(), random_); - // NOLINTNEXTLINE(cppcoreguidelines-narrowing-conversions) - any.erase(any.begin() + std::min(any.size(), lucky), any.end()); - } else { - any.clear(); - } - - auto make_send = [&]( - const Msg &msg, - const std::shared_ptr - &protocol) { - auto se = pm_->getStreamEngine(); - BOOST_ASSERT(se); - - auto message = - std::make_shared>>( - msg); - logger_->trace( - "Broadcasting messages.(relay_parent={}, group_size={}, " - "lucky_size={})", - relay_parent, - group.size(), - any.size()); - - for (auto &peer : group) { - SL_TRACE(logger_, "Send to peer from group. (peer={})", peer); - se->send(peer, protocol, message); - } - - for (auto &peer : any) { - SL_TRACE(logger_, "Send to peer from any. (peer={})", peer); - se->send(peer, protocol, message); - } - }; - - for (const network::VersionedValidatorProtocolMessage &msg : messages) { - visit_in_place( - msg, - [&](const kagome::network::vstaging::ValidatorProtocolMessage &m) { - make_send(m, router_->getValidationProtocolVStaging()); - }, - [&](const kagome::network::ValidatorProtocolMessage &m) { - make_send(m, router_->getValidationProtocol()); - }); - } - } - void ParachainProcessorImpl::process_vstaging_statement( const libp2p::peer::PeerId &peer_id, const network::vstaging::StatementDistributionMessage &msg) { @@ -2301,110 +2101,6 @@ namespace kagome::parachain { return sign_result.value(); } - template - bool ParachainProcessorImpl::tryOpenOutgoingCollatingStream( - const libp2p::peer::PeerId &peer_id, F &&callback) { - auto protocol = router_->getCollationProtocolVStaging(); - BOOST_ASSERT(protocol); - - return tryOpenOutgoingStream( - peer_id, std::move(protocol), std::forward(callback)); - } - - void ParachainProcessorImpl::sendMyView( - const libp2p::peer::PeerId &peer_id, - const std::shared_ptr &protocol) { - BOOST_ASSERT(protocol); - CHECK_OR_RET(canProcessParachains().has_value()); - - SL_DEBUG(logger_, - "Send my view.(peer={}, protocol={})", - peer_id, - protocol->protocolName()); - pm_->getStreamEngine()->send( - peer_id, - protocol, - std::make_shared< - network::WireMessage>( - network::ViewUpdate{ - .view = - network::View{ - .heads_ = block_tree_->getLeaves(), - .finalized_number_ = - block_tree_->getLastFinalized().number, - }, - })); - } - - void ParachainProcessorImpl::onIncomingCollationStream( - const libp2p::peer::PeerId &peer_id, network::CollationVersion version) { - REINVOKE(*main_pool_handler_, onIncomingCollationStream, peer_id, version); - - auto peer_state = [&]() { - auto res = pm_->getPeerState(peer_id); - if (!res) { - SL_TRACE(logger_, - "No PeerState of peer {}. Default one has created", - peer_id); - res = pm_->createDefaultPeerState(peer_id); - } - return res; - }(); - - peer_state->get().collation_version = version; - if (tryOpenOutgoingCollatingStream( - peer_id, [wptr{weak_from_this()}, peer_id, version]() { - TRY_GET_OR_RET(self, wptr.lock()); - switch (version) { - case network::CollationVersion::V1: - case network::CollationVersion::VStaging: { - self->sendMyView( - peer_id, self->router_->getCollationProtocolVStaging()); - } break; - default: { - UNREACHABLE; - } break; - } - })) { - SL_DEBUG(logger_, "Initiated collation protocol with {}", peer_id); - } - } - - void ParachainProcessorImpl::onIncomingValidationStream( - const libp2p::peer::PeerId &peer_id, network::CollationVersion version) { - REINVOKE(*main_pool_handler_, onIncomingValidationStream, peer_id, version); - - SL_TRACE(logger_, "Received incoming validation stream {}", peer_id); - auto peer_state = [&]() { - auto res = pm_->getPeerState(peer_id); - if (!res) { - SL_TRACE(logger_, - "No PeerState of peer {}. Default one has created", - peer_id); - res = pm_->createDefaultPeerState(peer_id); - } - return res; - }(); - - peer_state->get().collation_version = version; - if (tryOpenOutgoingValidationStream( - peer_id, version, [wptr{weak_from_this()}, peer_id, version]() { - TRY_GET_OR_RET(self, wptr.lock()); - switch (version) { - case network::CollationVersion::V1: - case network::CollationVersion::VStaging: { - self->sendMyView( - peer_id, self->router_->getValidationProtocolVStaging()); - } break; - default: { - UNREACHABLE; - } break; - } - })) { - logger_->debug("Initiated validation protocol with {}", peer_id); - } - } - network::ResponsePov ParachainProcessorImpl::getPov( CandidateHash &&candidate_hash) { if (auto res = av_store_->getPov(candidate_hash)) { @@ -2452,16 +2148,11 @@ namespace kagome::parachain { }; }); - pm_->getStreamEngine()->send( - peer_id, - router_->getCollationProtocolVStaging(), - std::make_shared< - network::WireMessage>( - network::vstaging::CollatorProtocolMessage( - network::vstaging::CollationMessage( - network::vstaging::CollatorProtocolMessageCollationSeconded{ - .relay_parent = relay_parent, - .statement = std::move(stm)})))); + router_->getCollationProtocol()->write(peer_id, + network::Seconded{ + .relay_parent = relay_parent, + .statement = std::move(stm), + }); } template diff --git a/core/parachain/validator/network_bridge.hpp b/core/parachain/validator/network_bridge.hpp index 6105365651..191df279f3 100644 --- a/core/parachain/validator/network_bridge.hpp +++ b/core/parachain/validator/network_bridge.hpp @@ -8,40 +8,22 @@ #include #include + #include "common/main_thread_pool.hpp" -#include "network/impl/stream_engine.hpp" -#include "network/peer_manager.hpp" -#include "network/protocol_base.hpp" -#include "utils/weak_macro.hpp" + +namespace libp2p::connection { + struct Stream; +} // namespace libp2p::connection namespace kagome::parachain { struct NetworkBridge : std::enable_shared_from_this { - NetworkBridge( - common::MainThreadPool &main_thread_pool, - std::shared_ptr peer_manager, - std::shared_ptr app_state_manager) - : main_pool_handler_(main_thread_pool.handler(*app_state_manager)), - pm_(std::move(peer_manager)) {} - - template - void send_to_peer(const libp2p::peer::PeerId &peer, - const std::shared_ptr &protocol, - const std::shared_ptr &message) { - REINVOKE(*main_pool_handler_, - send_to_peer, - peer, - protocol, - std::move(message)); - std::ignore = tryOpenOutgoingStream( - peer, protocol, [WEAK_SELF, peer, message, protocol]() { - WEAK_LOCK(self); - self->pm_->getStreamEngine()->send(peer, protocol, message); - }); - } + NetworkBridge(common::MainThreadPool &main_thread_pool, + application::AppStateManager &app_state_manager) + : main_pool_handler_{main_thread_pool.handler(app_state_manager)} {} template - void send_response(std::shared_ptr stream, + void send_response(std::shared_ptr stream, std::shared_ptr protocol, std::shared_ptr response) { REINVOKE(*main_pool_handler_, @@ -52,79 +34,9 @@ namespace kagome::parachain { protocol->writeResponseAsync(std::move(stream), std::move(*response)); } - template - requires std::is_same_v - void connect_to_peers(Container peers) { - REINVOKE(*main_pool_handler_, connect_to_peers, std::move(peers)); - for (const auto &peer : peers) { - pm_->connectToPeer(libp2p::peer::PeerInfo{.id = peer}); - } - } - - template - requires std::is_same_v - void send_to_peers(Container peers, - const std::shared_ptr &protocol, - const std::shared_ptr &message) { - REINVOKE(*main_pool_handler_, - send_to_peers, - std::move(peers), - protocol, - message); - for (const auto &peer : peers) { - send_to_peer(peer, protocol, message); - } - } - - private: - template - bool tryOpenOutgoingStream(const libp2p::peer::PeerId &peer_id, - std::shared_ptr protocol, - F &&callback) { - auto stream_engine = pm_->getStreamEngine(); - BOOST_ASSERT(stream_engine); - - if (stream_engine->reserveOutgoing(peer_id, protocol)) { - protocol->newOutgoingStream( - peer_id, - [callback = std::forward(callback), - protocol, - peer_id, - wptr{weak_from_this()}](auto &&stream_result) mutable { - auto self = wptr.lock(); - if (not self) { - return; - } - - auto stream_engine = self->pm_->getStreamEngine(); - stream_engine->dropReserveOutgoing(peer_id, protocol); - - if (!stream_result.has_value()) { - self->logger->trace("Unable to create stream {} with {}: {}", - protocol->protocolName(), - peer_id, - stream_result.error()); - return; - } - - auto stream = stream_result.value(); - stream_engine->addOutgoing(std::move(stream_result.value()), - protocol); - - std::forward(callback)(); - }); - return true; - } - std::forward(callback)(); - return false; - } - private: log::Logger logger = log::createLogger("NetworkBridge", "parachain"); std::shared_ptr main_pool_handler_; - std::shared_ptr pm_; }; } // namespace kagome::parachain diff --git a/core/parachain/validator/parachain_processor.hpp b/core/parachain/validator/parachain_processor.hpp index 46aabaa007..29a50db570 100644 --- a/core/parachain/validator/parachain_processor.hpp +++ b/core/parachain/validator/parachain_processor.hpp @@ -22,7 +22,6 @@ #include "consensus/timeline/slots_util.hpp" #include "crypto/hasher.hpp" #include "metrics/metrics.hpp" -#include "network/impl/stream_engine.hpp" #include "network/peer_manager.hpp" #include "network/peer_view.hpp" #include "network/protocols/req_collation_protocol.hpp" @@ -35,6 +34,7 @@ #include "parachain/backing/store.hpp" #include "parachain/pvf/precheck.hpp" #include "parachain/pvf/pvf.hpp" +#include "parachain/validator/backed_candidates_source.hpp" #include "parachain/validator/backing_implicit_view.hpp" #include "parachain/validator/collations.hpp" #include "parachain/validator/prospective_parachains/prospective_parachains.hpp" @@ -117,16 +117,10 @@ struct std::hash { }; namespace kagome::parachain { - - struct BackedCandidatesSource { - virtual ~BackedCandidatesSource() {} - virtual std::vector getBackedCandidates( - const RelayHash &relay_parent) = 0; - }; - - struct ParachainProcessorImpl - : BackedCandidatesSource, - std::enable_shared_from_this { + class ParachainProcessorImpl + : public std::enable_shared_from_this, + public BackedCandidatesSource { + public: enum class Error { RESPONSE_ALREADY_RECEIVED = 1, COLLATION_NOT_FOUND, @@ -246,27 +240,6 @@ namespace kagome::parachain { network::CollatorPublicKey pubkey, network::ParachainId para_id); - /** - * @brief Handles an incoming collation stream from a peer. - * - * @param peer_id The ID of the peer from which the collation stream is - * received. - * @param version The version of the collation protocol used in the stream. - */ - void onIncomingCollationStream(const libp2p::peer::PeerId &peer_id, - network::CollationVersion version); - - /** - * @brief Handles an incoming validation stream from a peer. - * - * @param peer_id The ID of the peer from which the validation stream is - * received. - * @param version The version of the collation protocol used in the - * validation stream. - */ - void onIncomingValidationStream(const libp2p::peer::PeerId &peer_id, - network::CollationVersion version); - void onValidationProtocolMsg( const libp2p::peer::PeerId &peer_id, const network::VersionedValidatorProtocolMessage &message); @@ -506,9 +479,6 @@ namespace kagome::parachain { outcome::result getBabeRandomness( const RelayHash &relay_parent); - void send_to_validators_group( - const RelayHash &relay_parent, - const std::deque &messages); /** * @brief Inserts an advertisement into the peer's data. @@ -677,16 +647,6 @@ namespace kagome::parachain { /* * Notification */ - void broadcastView(const network::View &view) const; - void broadcastViewToGroup(const primitives::BlockHash &relay_parent, - const network::View &view); - void broadcastViewExcept(const libp2p::peer::PeerId &peer_id, - const network::View &view) const; - template - void notify_internal(std::shared_ptr &context, F &&func) { - BOOST_ASSERT(context); - boost::asio::post(*context, std::forward(func)); - } void notifyAvailableData(std::vector &&chunk_list, const primitives::BlockHash &relay_parent, const network::CandidateHash &candidate_hash, @@ -771,78 +731,13 @@ namespace kagome::parachain { const primitives::BlockHash &relay_parent, const ProspectiveParachainsModeOpt &mode); - void spawn_and_update_peer( - std::unordered_set &cache, - const primitives::AuthorityDiscoveryId &id); - - template - bool tryOpenOutgoingCollatingStream(const libp2p::peer::PeerId &peer_id, - - F &&callback); - - public: - template - bool tryOpenOutgoingValidationStream(const libp2p::peer::PeerId &peer_id, - network::CollationVersion version, - F &&callback) { - auto protocol = router_->getValidationProtocolVStaging(); - BOOST_ASSERT(protocol); - - return tryOpenOutgoingStream( - peer_id, std::move(protocol), std::forward(callback)); - } - private: - template - bool tryOpenOutgoingStream(const libp2p::peer::PeerId &peer_id, - std::shared_ptr protocol, - F &&callback) { - auto stream_engine = pm_->getStreamEngine(); - BOOST_ASSERT(stream_engine); - - if (stream_engine->reserveOutgoing(peer_id, protocol)) { - protocol->newOutgoingStream( - peer_id, - [callback = std::forward(callback), - protocol, - peer_id, - wptr{weak_from_this()}](auto &&stream_result) mutable { - auto self = wptr.lock(); - if (not self) { - return; - } - - auto stream_engine = self->pm_->getStreamEngine(); - stream_engine->dropReserveOutgoing(peer_id, protocol); - - if (!stream_result.has_value()) { - self->logger_->verbose("Unable to create stream {} with {}: {}", - protocol->protocolName(), - peer_id, - stream_result.error()); - return; - } - - auto stream = stream_result.value(); - stream_engine->addOutgoing(std::move(stream_result.value()), - protocol); - - std::forward(callback)(); - }); - return true; - } - std::forward(callback)(); - return false; - } - outcome::result enqueueCollation( const RelayHash &relay_parent, ParachainId para_id, const libp2p::peer::PeerId &peer_id, const CollatorId &collator_id, std::optional> &&prospective_candidate); - void sendMyView(const libp2p::peer::PeerId &peer_id, - const std::shared_ptr &protocol); bool isValidatingNode() const; bool canSecond(ParachainId candidate_para_id, @@ -911,11 +806,12 @@ namespace kagome::parachain { std::shared_ptr parachain_host_; const application::AppConfiguration &app_config_; primitives::events::SyncStateSubscriptionEnginePtr sync_state_observable_; - primitives::events::SyncStateEventSubscriberPtr sync_state_observer_; + std::shared_ptr sync_state_observer_; std::shared_ptr query_audi_; LazySPtr slots_util_; std::shared_ptr babe_config_repo_; + bool synchronized_ = false; primitives::events::ChainSub chain_sub_; std::shared_ptr worker_pool_handler_; std::default_random_engine random_; diff --git a/core/parachain/validator/statement_distribution/peer_state.hpp b/core/parachain/validator/statement_distribution/peer_state.hpp index 295b45d425..2e11d85579 100644 --- a/core/parachain/validator/statement_distribution/peer_state.hpp +++ b/core/parachain/validator/statement_distribution/peer_state.hpp @@ -20,7 +20,7 @@ #include "utils/pool_handler_ready_make.hpp" namespace kagome::parachain { - struct ParachainProcessorImpl; + class ParachainProcessorImpl; } namespace kagome::parachain::statement_distribution { diff --git a/core/parachain/validator/statement_distribution/statement_distribution.cpp b/core/parachain/validator/statement_distribution/statement_distribution.cpp index 6970b6787e..c177bb9829 100644 --- a/core/parachain/validator/statement_distribution/statement_distribution.cpp +++ b/core/parachain/validator/statement_distribution/statement_distribution.cpp @@ -4,10 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include +#include "parachain/validator/statement_distribution/statement_distribution.hpp" + #include "network/impl/protocols/fetch_attested_candidate.hpp" -#include "network/impl/protocols/parachain_protocols.hpp" +#include "network/impl/protocols/parachain.hpp" #include "parachain/validator/parachain_processor.hpp" +#include "utils/weak_macro.hpp" #define COMPONENT_NAME "StatementDistribution" @@ -117,10 +119,8 @@ namespace kagome::parachain::statement_distribution { LazySPtr _slots_util, std::shared_ptr _babe_config_repo, primitives::events::PeerSubscriptionEnginePtr _peer_events_engine) - : implicit_view(_prospective_parachains, - _parachain_host, - _block_tree, - std::nullopt), + : implicit_view( + _prospective_parachains, _parachain_host, _block_tree, std::nullopt), per_session(RefCache::create()), signer_factory(std::move(sf)), peer_use_count( @@ -144,11 +144,11 @@ namespace kagome::parachain::statement_distribution { babe_config_repo(std::move(_babe_config_repo)), peer_state_sub( std::make_shared( - std::move(_peer_events_engine), false)), + std::move(_peer_events_engine))), my_view_sub(std::make_shared( - _peer_view->getMyViewObservable(), false)), + _peer_view->getMyViewObservable())), remote_view_sub(std::make_shared( - _peer_view->getRemoteViewObservable(), false)) { + _peer_view->getRemoteViewObservable())) { BOOST_ASSERT(per_session); BOOST_ASSERT(signer_factory); BOOST_ASSERT(peer_use_count); @@ -175,27 +175,26 @@ namespace kagome::parachain::statement_distribution { primitives::events::subscribe( *remote_view_sub, network::PeerView::EventType::kViewUpdated, - [wptr{weak_from_this()}](const libp2p::peer::PeerId &peer_id, - const network::View &view) { - TRY_GET_OR_RET(self, wptr.lock()); + [WEAK_SELF](const libp2p::peer::PeerId &peer_id, + const network::View &view) { + WEAK_LOCK(self); self->handle_peer_view_update(peer_id, view); }); - peer_state_sub->setCallback( - [wptr{weak_from_this()}](subscription::SubscriptionSetId, - auto &, - const auto ev_key, - const libp2p::peer::PeerId &peer) { - TRY_GET_OR_RET(self, wptr.lock()); - switch (ev_key) { - case primitives::events::PeerEventType::kConnected: - return self->on_peer_connected(peer); - case primitives::events::PeerEventType::kDisconnected: - return self->on_peer_disconnected(peer); - default: - break; - } - }); + peer_state_sub->setCallback([WEAK_SELF](subscription::SubscriptionSetId, + auto &, + const auto ev_key, + const libp2p::peer::PeerId &peer) { + WEAK_LOCK(self); + switch (ev_key) { + case primitives::events::PeerEventType::kConnected: + return self->on_peer_connected(peer); + case primitives::events::PeerEventType::kDisconnected: + return self->on_peer_disconnected(peer); + default: + break; + } + }); peer_state_sub->subscribe(peer_state_sub->generateSubscriptionSetId(), primitives::events::PeerEventType::kConnected); peer_state_sub->subscribe(peer_state_sub->generateSubscriptionSetId(), @@ -204,8 +203,8 @@ namespace kagome::parachain::statement_distribution { primitives::events::subscribe( *my_view_sub, network::PeerView::EventType::kViewUpdated, - [wptr{weak_from_this()}](const network::ExView &event) { - TRY_GET_OR_RET(self, wptr.lock()); + [WEAK_SELF](const network::ExView &event) { + WEAK_LOCK(self); if (auto result = self->handle_view_event(event); result.has_error()) { SL_ERROR(self->logger, @@ -311,13 +310,6 @@ namespace kagome::parachain::statement_distribution { event.new_head.hash(), res.error()); } - if (auto res = update_our_view(event.new_head.hash(), event.view); - res.has_error()) { - SL_ERROR(logger, - "Update our view failed. (relay parent={}, error={})", - event.new_head.hash(), - res.error()); - } } outcome::result @@ -530,80 +522,6 @@ namespace kagome::parachain::statement_distribution { return outcome::success(); } - outcome::result StatementDistribution::update_our_view( - const Hash &relay_parent, const network::View &view) { - if (auto parachain_proc = parachain_processor.lock()) { - OUTCOME_TRY(parachain_proc->canProcessParachains()); - } - - OUTCOME_TRY(per_relay_parent, getStateByRelayParent(relay_parent)); - - std::unordered_set peers_to_send; - const auto &per_session_state = - per_relay_parent.get().per_session_state->value(); - const auto &local_validator = per_session_state.local_validator; - - if (local_validator) { - if (const auto our_group = - per_session_state.groups.byValidatorIndex(*local_validator)) { - /// update peers of our group - if (const auto group = per_session_state.groups.get(*our_group)) { - for (const auto vi : *group) { - if (auto peer = query_audi->get( - per_session_state.session_info.discovery_keys[vi])) { - peers_to_send.emplace(peer->id); - } else { - SL_TRACE(logger, - "No audi for {}.", - per_session_state.session_info.discovery_keys[vi]); - } - } - } - } - } - - /// update peers in grid view - if (per_session_state.grid_view) { - for (const auto &view : *per_session_state.grid_view) { - for (const auto vi : view.sending) { - if (auto peer = query_audi->get( - per_session_state.session_info.discovery_keys[vi])) { - peers_to_send.emplace(peer->id); - } else { - SL_TRACE(logger, - "No audi for {}.", - per_session_state.session_info.discovery_keys[vi]); - } - } - for (const auto vi : view.receiving) { - if (auto peer = query_audi->get( - per_session_state.session_info.discovery_keys[vi])) { - peers_to_send.emplace(peer->id); - } else { - SL_TRACE(logger, - "No audi for {}.", - per_session_state.session_info.discovery_keys[vi]); - } - } - } - } - - for (const auto &[peer, _] : peers) { - peers_to_send.emplace(peer); - } - - SL_INFO(logger, "Send my view. (peers_count={})", peers_to_send.size()); - auto message = std::make_shared< - network::WireMessage>( - network::ViewUpdate{ - .view = view, - }); - network_bridge->connect_to_peers(peers_to_send); - network_bridge->send_to_peers( - peers_to_send, router->getValidationProtocolVStaging(), message); - return outcome::success(); - } - std::unordered_map> StatementDistribution::determine_groups_per_para( const std::vector &availability_cores, @@ -690,7 +608,7 @@ namespace kagome::parachain::statement_distribution { // outcome::result void StatementDistribution::OnFetchAttestedCandidateRequest( const network::vstaging::AttestedCandidateRequest &request, - std::shared_ptr stream) { + std::shared_ptr stream) { REINVOKE(*statements_distribution_thread_handler, OnFetchAttestedCandidateRequest, request, @@ -1075,13 +993,13 @@ namespace kagome::parachain::statement_distribution { .candidate_hash = candidate_hash, .mask = unwanted_mask, }, - [wptr{weak_from_this()}, + [WEAK_SELF, relay_parent{relay_parent}, candidate_hash{candidate_hash}, group_index{group_index}]( outcome::result r) mutable { - TRY_GET_OR_RET(self, wptr.lock()); + WEAK_LOCK(self); self->handle_response( std::move(r), relay_parent, candidate_hash, group_index); }); @@ -1319,15 +1237,7 @@ namespace kagome::parachain::statement_distribution { SL_TRACE(logger, "Sending messages. (relay_parent = {})", relay_parent); for (auto &msg : messages) { - if (auto m = if_type(msg)) { - auto message = std::make_shared< - network::WireMessage>( - std::move(m->get())); - network_bridge->send_to_peer( - peer_id, router->getValidationProtocolVStaging(), message); - } else { - assert(false); - } + router->getValidationProtocol()->write(peer_id, msg); } } @@ -1474,16 +1384,7 @@ namespace kagome::parachain::statement_distribution { manifest.relay_parent, manifest.candidate_hash); for (auto &[peers, msg] : messages) { - if (auto m = - if_type(msg)) { - auto message = std::make_shared>( - std::move(m->get())); - network_bridge->send_to_peers( - peers, router->getValidationProtocolVStaging(), message); - } else { - assert(false); - } + router->getValidationProtocol()->write(peers, msg); } } else if (!candidates.is_confirmed(manifest.candidate_hash)) { SL_TRACE( @@ -1953,14 +1854,7 @@ namespace kagome::parachain::statement_distribution { "Sending manifest to v2 peers. (candidate_hash={}, n_peers={})", candidate_hash, manifest_peers.size()); - auto message = std::make_shared< - network::WireMessage>( - kagome::network::vstaging::ValidatorProtocolMessage{ - kagome::network::vstaging::StatementDistributionMessage{ - manifest}}); - - network_bridge->send_to_peers( - manifest_peers, router->getValidationProtocolVStaging(), message); + router->getValidationProtocol()->write(manifest_peers, manifest); } if (!ack_peers.empty()) { @@ -1969,13 +1863,7 @@ namespace kagome::parachain::statement_distribution { "n_peers={})", candidate_hash, ack_peers.size()); - auto message = std::make_shared< - network::WireMessage>( - kagome::network::vstaging::ValidatorProtocolMessage{ - kagome::network::vstaging::StatementDistributionMessage{ - acknowledgement}}); - network_bridge->send_to_peers( - ack_peers, router->getValidationProtocolVStaging(), message); + router->getValidationProtocol()->write(ack_peers, acknowledgement); } if (!post_statements.empty()) { @@ -1986,16 +1874,7 @@ namespace kagome::parachain::statement_distribution { post_statements.size()); for (auto &[peers, msg] : post_statements) { - if (auto m = - if_type(msg)) { - auto message = std::make_shared>( - std::move(m->get())); - network_bridge->send_to_peers( - peers, router->getValidationProtocolVStaging(), message); - } else { - assert(false); - } + router->getValidationProtocol()->write(peers, msg); } } } @@ -2455,23 +2334,16 @@ namespace kagome::parachain::statement_distribution { } } - auto message_v2 = std::make_shared< - network::WireMessage>( - kagome::network::vstaging::ValidatorProtocolMessage{ - kagome::network::vstaging::StatementDistributionMessage{ - kagome::network::vstaging:: - StatementDistributionMessageStatement{ - .relay_parent = relay_parent, - .compact = statement, - }}}); + network::vstaging::StatementDistributionMessageStatement message_v2{ + .relay_parent = relay_parent, + .compact = statement, + }; SL_TRACE( logger, "Send statements to validators. (relay_parent={}, validators_count={})", relay_parent, statement_to_peers.size()); - network_bridge->send_to_peers(statement_to_peers, - router->getValidationProtocolVStaging(), - message_v2); + router->getValidationProtocol()->write(statement_to_peers, message_v2); } void StatementDistribution::share_local_statement( @@ -2721,15 +2593,7 @@ namespace kagome::parachain::statement_distribution { } for (auto &[peers, msg] : messages) { - if (auto m = if_type(msg)) { - auto message = std::make_shared< - network::WireMessage>( - std::move(m->get())); - network_bridge->send_to_peers( - peers, router->getValidationProtocolVStaging(), message); - } else { - assert(false); - } + router->getValidationProtocol()->write(peers, msg); } } @@ -2770,15 +2634,7 @@ namespace kagome::parachain::statement_distribution { } for (auto &[peers, msg] : messages) { - if (auto m = if_type(msg)) { - auto message = std::make_shared< - network::WireMessage>( - std::move(m->get())); - network_bridge->send_to_peers( - peers, router->getValidationProtocolVStaging(), message); - } else { - BOOST_ASSERT(false); - } + router->getValidationProtocol()->write(peers, msg); } } diff --git a/core/parachain/validator/statement_distribution/statement_distribution.hpp b/core/parachain/validator/statement_distribution/statement_distribution.hpp index 35c7ca81c0..cad21d2bec 100644 --- a/core/parachain/validator/statement_distribution/statement_distribution.hpp +++ b/core/parachain/validator/statement_distribution/statement_distribution.hpp @@ -27,14 +27,15 @@ #include "utils/pool_handler_ready_make.hpp" namespace kagome::parachain { - struct ParachainProcessorImpl; + class ParachainProcessorImpl; } namespace kagome::parachain::statement_distribution { - struct StatementDistribution - : std::enable_shared_from_this, - network::CanDisconnect { + class StatementDistribution + : public std::enable_shared_from_this, + public network::CanDisconnect { + public: enum class Error : uint8_t { RESPONSE_ALREADY_RECEIVED = 1, COLLATION_NOT_FOUND, @@ -96,7 +97,7 @@ namespace kagome::parachain::statement_distribution { // outcome::result void OnFetchAttestedCandidateRequest( const network::vstaging::AttestedCandidateRequest &request, - std::shared_ptr stream); + std::shared_ptr stream); // CanDisconnect bool can_disconnect(const libp2p::PeerId &) const override; @@ -339,8 +340,6 @@ namespace kagome::parachain::statement_distribution { std::vector new_contexts); outcome::result handle_deactive_leaves_update_inner( const std::vector &lost); - outcome::result update_our_view(const Hash &relay_parent, - const network::View &view); void on_peer_connected(const libp2p::peer::PeerId &peer); void on_peer_disconnected(const libp2p::peer::PeerId &peer); diff --git a/core/primitives/event_types.hpp b/core/primitives/event_types.hpp index 03e7422668..4c09737071 100644 --- a/core/primitives/event_types.hpp +++ b/core/primitives/event_types.hpp @@ -11,7 +11,9 @@ #include #include +#include #include +#include #include "common/buffer.hpp" #include "consensus/timeline/sync_state.hpp" @@ -243,7 +245,7 @@ namespace kagome::primitives::events { using PeerSubscriptionEngine = subscription::SubscriptionEngine; using PeerSubscriptionEnginePtr = std::shared_ptr; using PeerEventSubscriber = PeerSubscriptionEngine::SubscriberType; @@ -259,7 +261,7 @@ namespace kagome::primitives::events { using SyncStateSubscriptionEngine = subscription::SubscriptionEngine< primitives::events::SyncStateEventType, - bool, + std::monostate, primitives::events::SyncStateEventParams>; using SyncStateSubscriptionEnginePtr = std::shared_ptr; @@ -284,14 +286,28 @@ namespace kagome::primitives::events { [f{std::move(f)}](subscription::SubscriptionSetId, Receiver &, EventKey, - const Arguments &...args) { f(args...); }); + const Arguments &...args) mutable { f(args...); }); sub.subscribe(sub.generateSubscriptionSetId(), type); } + template + auto subscribe( + std::shared_ptr< + subscription::SubscriptionEngine> + engine, + EventKey type, + auto f) { + auto sub = std::make_shared< + typename decltype(engine)::element_type::SubscriberType>( + std::move(engine)); + subscribe(*sub, type, std::move(f)); + return sub; + } + struct ChainSub { ChainSub(ChainSubscriptionEnginePtr engine) : sub{std::make_shared( - std::move(engine))} {} + std::move(engine))} {} void onBlock(ChainEventType type, auto f) { subscribe(*sub, type, [f{std::move(f)}](const ChainEventParams &args) { @@ -319,4 +335,18 @@ namespace kagome::primitives::events { ChainEventSubscriberPtr sub; }; + + std::shared_ptr onSync(SyncStateSubscriptionEnginePtr engine, auto f) { + return subscribe( + std::move(engine), + SyncStateEventType::kSyncState, + libp2p::SharedFn{[f_{std::make_optional(std::move(f))}]( + const SyncStateEventParams &event) mutable { + if (event == consensus::SyncState::SYNCHRONIZED) { + if (auto f = qtils::optionTake(f_)) { + (*f)(); + } + } + }}); + } } // namespace kagome::primitives::events diff --git a/core/runtime/common/runtime_upgrade_tracker_impl.cpp b/core/runtime/common/runtime_upgrade_tracker_impl.cpp index 6348a2a756..02d13a97a1 100644 --- a/core/runtime/common/runtime_upgrade_tracker_impl.cpp +++ b/core/runtime/common/runtime_upgrade_tracker_impl.cpp @@ -193,13 +193,8 @@ namespace kagome::runtime { BOOST_ASSERT(block_tree != nullptr); block_tree_ = block_tree; - chain_subscription_ = - std::make_shared( - chain_sub_engine); - BOOST_ASSERT(chain_subscription_ != nullptr); - - primitives::events::subscribe( - *chain_subscription_, + chain_subscription_ = primitives::events::subscribe( + chain_sub_engine, primitives::events::ChainEventType::kNewRuntime, [this](const primitives::events::ChainEventParams &event_params) { const auto &block_hash = diff --git a/core/telemetry/impl/service_impl.cpp b/core/telemetry/impl/service_impl.cpp index 77dc66f8d3..530aa026fd 100644 --- a/core/telemetry/impl/service_impl.cpp +++ b/core/telemetry/impl/service_impl.cpp @@ -48,14 +48,14 @@ namespace kagome::telemetry { const libp2p::Host &host, std::shared_ptr tx_pool, std::shared_ptr storage, - std::shared_ptr peer_manager, + PeerCount peer_count, TelemetryThreadPool &telemetry_thread_pool) : app_configuration_{app_configuration}, chain_spec_{chain_spec}, host_{host}, tx_pool_{std::move(tx_pool)}, buffer_storage_{storage->getSpace(storage::Space::kDefault)}, - peer_manager_{std::move(peer_manager)}, + peer_count_{std::move(peer_count)}, io_context_{telemetry_thread_pool.io_context()}, scheduler_{std::make_shared( std::make_shared( @@ -65,7 +65,6 @@ namespace kagome::telemetry { log_{log::createLogger("TelemetryService", "telemetry")} { BOOST_ASSERT(tx_pool_); BOOST_ASSERT(buffer_storage_); - BOOST_ASSERT(peer_manager_); if (enabled_) { pool_handler_ = poolHandlerReadyMake( this, app_state_manager, telemetry_thread_pool, log_); @@ -451,7 +450,7 @@ namespace kagome::telemetry { rapidjson::Value payload(rapidjson::kObjectType); rapidjson::Value peers_count; - peers_count.SetUint(peer_manager_->activePeersNumber()); + peers_count.SetUint(*peer_count_.v); auto bandwidth = getBandwidth(); rapidjson::Value upBandwidth, downBandwidth; diff --git a/core/telemetry/impl/service_impl.hpp b/core/telemetry/impl/service_impl.hpp index 192c1a38f7..cd5b8b59d7 100644 --- a/core/telemetry/impl/service_impl.hpp +++ b/core/telemetry/impl/service_impl.hpp @@ -28,10 +28,10 @@ namespace rapidjson { #include "application/chain_spec.hpp" #include "common/spin_lock.hpp" #include "log/logger.hpp" -#include "network/peer_manager.hpp" #include "storage/spaced_storage.hpp" #include "telemetry/connection.hpp" #include "telemetry/impl/message_pool.hpp" +#include "telemetry/peer_count.hpp" #include "transaction_pool/transaction_pool.hpp" namespace kagome { @@ -63,7 +63,7 @@ namespace kagome::telemetry { const libp2p::Host &host, std::shared_ptr tx_pool, std::shared_ptr storage, - std::shared_ptr peer_manager, + PeerCount peer_count, TelemetryThreadPool &telemetry_thread_pool); TelemetryServiceImpl(const TelemetryServiceImpl &) = delete; TelemetryServiceImpl(TelemetryServiceImpl &&) = delete; @@ -146,7 +146,7 @@ namespace kagome::telemetry { const libp2p::Host &host_; std::shared_ptr tx_pool_; std::shared_ptr buffer_storage_; - std::shared_ptr peer_manager_; + PeerCount peer_count_; std::shared_ptr pool_handler_; std::shared_ptr io_context_; std::shared_ptr scheduler_; diff --git a/core/telemetry/peer_count.hpp b/core/telemetry/peer_count.hpp new file mode 100644 index 0000000000..bd55625c8b --- /dev/null +++ b/core/telemetry/peer_count.hpp @@ -0,0 +1,23 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +namespace kagome::telemetry { + /** + * Counter for "peers" telemetry value. + * `BlockAnnounceProtocol` writes value. + * `TelemetryServiceImpl` reads value. + */ + struct PeerCount { + using T = std::atomic_size_t; + PeerCount() : v{std::make_shared()} {} + std::shared_ptr v; + }; +} // namespace kagome::telemetry diff --git a/core/transaction_pool/impl/transaction_pool_impl.cpp b/core/transaction_pool/impl/transaction_pool_impl.cpp index ee39cd463c..4ddb974713 100644 --- a/core/transaction_pool/impl/transaction_pool_impl.cpp +++ b/core/transaction_pool/impl/transaction_pool_impl.cpp @@ -123,8 +123,7 @@ namespace kagome::transaction_pool { OUTCOME_TRY(tx, constructTransaction(source, extrinsic, hash)); if (tx.should_propagate) { - std::vector txs{tx}; - tx_transmitter_->propagateTransactions(txs); + tx_transmitter_->propagateTransaction(tx); } OUTCOME_TRY( diff --git a/core/utils/lru.hpp b/core/utils/lru.hpp index db5a4cae0c..d376b3d6b1 100644 --- a/core/utils/lru.hpp +++ b/core/utils/lru.hpp @@ -7,6 +7,10 @@ #pragma once #include +#include +#include +#include +#include #include namespace kagome { @@ -171,4 +175,47 @@ namespace kagome { Lru lru_; }; + + /** + * Wraps `map>` with `capacity` for `LruSet`. + */ + template + class MapLruSet { + public: + explicit MapLruSet(size_t capacity) : capacity_{capacity} {} + + /** + * @returns true if `K` was added. + */ + bool add(const K &k) { + auto it = map_.find(k); + if (it != map_.end()) { + return false; + } + it = map_.emplace(k, capacity_).first; + return true; + } + + /** + * @returns true if `(K, V)` was added. + */ + bool add(const K &k, const V &v) { + auto it = map_.find(k); + if (it == map_.end()) { + it = map_.emplace(k, capacity_).first; + } + return it->second.add(v); + } + + /** + * Remove `K` and all corresponding `V`. + */ + void remove(const K &k) { + map_.erase(k); + } + + private: + size_t capacity_; + std::unordered_map> map_{}; + }; } // namespace kagome diff --git a/core/utils/map_entry.hpp b/core/utils/map_entry.hpp new file mode 100644 index 0000000000..8d8070b1da --- /dev/null +++ b/core/utils/map_entry.hpp @@ -0,0 +1,76 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include +#include + +namespace kagome { + template + struct MapEntry { + using I = typename M::iterator; + using K = typename M::key_type; + + MapEntry(M &map, const K &key) : map{map} { + if (auto it = map.find(key); it != map.end()) { + it_or_key = it; + } else { + it_or_key = key; + } + } + + bool has() const { + return std::holds_alternative(it_or_key); + } + operator bool() const { + return has(); + } + auto &operator*() { + assert(has()); + return std::get(it_or_key)->second; + } + auto *operator->() { + assert(has()); + return &std::get(it_or_key)->second; + } + void insert(M::mapped_type value) { + assert(not has()); + it_or_key = + map.emplace(std::move(std::get(it_or_key)), std::move(value)) + .first; + } + void insert_or_assign(M::mapped_type value) { + if (not has()) { + insert(std::move(value)); + } else { + **this = std::move(value); + } + } + M::mapped_type remove() { + assert(has()); + auto node = map.extract(std::get(it_or_key)); + it_or_key = std::move(node.key()); + return std::move(node.mapped()); + } + + // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members) + M ↦ + std::variant it_or_key{}; + }; + + template + auto entry(std::map &map, const K &key) { + return MapEntry>{map, key}; + } + + template + auto entry(std::unordered_map &map, const K &key) { + return MapEntry>{map, key}; + } +} // namespace kagome \ No newline at end of file diff --git a/core/utils/pool_handler.hpp b/core/utils/pool_handler.hpp index 7447390681..c23038988e 100644 --- a/core/utils/pool_handler.hpp +++ b/core/utils/pool_handler.hpp @@ -95,6 +95,11 @@ namespace kagome { } \ }) +#define EXPECT_THREAD(ctx) \ + if (not runningInThisThread(ctx)) throw std::logic_error { \ + "expected to execute on other thread" \ + } + /// Reinvokes function once depending on `template ` argument. /// If `true` reinvoke takes place, otherwise direct call. After reinvoke called /// function has `false` in kReinvoke. diff --git a/core/utils/try.hpp b/core/utils/try.hpp new file mode 100644 index 0000000000..a782d20900 --- /dev/null +++ b/core/utils/try.hpp @@ -0,0 +1,14 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#define TRY_FALSE(expr) \ + ({ \ + auto r = (expr); \ + if (not r) return false; \ + std::move(r.value()); \ + }) diff --git a/core/utils/with_type.hpp b/core/utils/with_type.hpp new file mode 100644 index 0000000000..c86c743446 --- /dev/null +++ b/core/utils/with_type.hpp @@ -0,0 +1,44 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +namespace kagome { + template + auto withType(size_t i, const auto &f) { + if (i == I) { + return f.template operator()(); + } + if constexpr (sizeof...(Ts) != 0) { + return withType(i, f); + } + throw std::out_of_range{"withType"}; + } + + /** + * Calls `f` with `i`th type. + */ + template + auto withType(size_t i, const auto &f) { + return withType<0, T...>(i, f); + } + + /** + * Wraps types for indexing. + */ + template + struct WithType { + /** + * Calls `withType` without specifying types explicitly. + */ + static auto with(size_t i, const auto &f) { + return withType(i, f); + } + }; +} // namespace kagome diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000000..e47685504b --- /dev/null +++ b/flake.lock @@ -0,0 +1,36 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1720279313, + "narHash": "sha256-POof6A43PHty3CgR67ADZEQRuPWX9FhL0sMzp3a4jOE=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/270dace49bc95a7f88ad187969179ff0d2ba20ed.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/270dace49bc95a7f88ad187969179ff0d2ba20ed.tar.gz" + } + }, + "oldnixpkgs": { + "locked": { + "lastModified": 1687213506, + "narHash": "sha256-6NfYqgF0XVc1zscPJfDE2UipOt3LUbXyf5DTUvr9zWs=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/c6c17387f7dd5332bec72bb9e76df434ac6b2bff.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/c6c17387f7dd5332bec72bb9e76df434ac6b2bff.tar.gz" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs", + "oldnixpkgs": "oldnixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000000..f98d1e7c60 --- /dev/null +++ b/flake.nix @@ -0,0 +1,63 @@ +{ + description = "A Nix-flake for building Kagome"; + + inputs.oldnixpkgs.url = + "https://github.com/NixOS/nixpkgs/archive/c6c17387f7dd5332bec72bb9e76df434ac6b2bff.tar.gz"; + inputs.nixpkgs.url = + "https://github.com/NixOS/nixpkgs/archive/270dace49bc95a7f88ad187969179ff0d2ba20ed.tar.gz"; + + outputs = { self, nixpkgs, oldnixpkgs }: + let + supportedSystems = + [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; + forEachSupportedSystem = f: + nixpkgs.lib.genAttrs supportedSystems (system: + f { + pkgs = import nixpkgs { inherit system; }; + oldpkgs = import oldnixpkgs { inherit system; }; + }); + in { + devShells = forEachSupportedSystem ({ pkgs, oldpkgs }: { + default = pkgs.mkShell.override { + # Override stdenv in order to change compiler: + # stdenv = pkgs.clangStdenv; + } { + packages = with pkgs; [ + clang-tools + oldpkgs.cmake + libseccomp + ncurses.dev + + # Rust tools + rustup + cargo + rust-analyzer + ]; + + shellHook = '' + export RUSTUP_HOME="$PWD/.rustup" + export CARGO_HOME="$PWD/.cargo" + export PATH="$CARGO_HOME/bin:$PATH" + export FLAKE_INITIATED=1 + + echo "Using RUSTUP_HOME: $RUSTUP_HOME" + echo "Using CARGO_HOME: $CARGO_HOME" + echo "Current PATH: $PATH" + + export RUST_VERSION=1.77.0 + + if ! rustup show | grep -q "$RUST_VERSION"; then + echo "Installing Rust version $RUST_VERSION" + rustup toolchain install $RUST_VERSION + rustup default $RUST_VERSION + else + echo "Rust version $RUST_VERSION already installed and set as default." + fi + + rustup show + ''; + + }; + }); + }; +} diff --git a/scripts/.env b/scripts/.env index 5dd6309bad..c820210f19 100644 --- a/scripts/.env +++ b/scripts/.env @@ -1,7 +1,3 @@ -RUST_VERSION=1.77.0 -RUSTUP_HOME=~/.rustup -CARGO_HOME=~/.cargo - CMAKE_VERSION="3.25" CMAKE_CORE_NUMBER=6 diff --git a/scripts/.rust_env b/scripts/.rust_env new file mode 100644 index 0000000000..4f30e442fc --- /dev/null +++ b/scripts/.rust_env @@ -0,0 +1,5 @@ +RUST_VERSION=1.77.0 +RUSTUP_HOME=~/.rustup +CARGO_HOME=~/.cargo + +PATH="${CARGO_HOME}/bin:${PATH}" diff --git a/scripts/build.sh b/scripts/build.sh old mode 100644 new mode 100755 index dd99787a95..4c616eb0f3 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -4,8 +4,16 @@ current_dir=$(dirname $(readlink -f "$0")) parent_dir=$(dirname "$current_dir") cd $parent_dir -set -a; source $current_dir/.env; set +a #include .env vars +# include .env vars and optionally .rust_env vars +set -a +if [ -z "$FLAKE_INITIATED" ]; then + source $current_dir/.rust_env +fi +source $current_dir/.env +set +a source $parent_dir/venv/bin/activate -$parent_dir/venv/bin/cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Release -$parent_dir/venv/bin/cmake --build build --target kagome -j ${CMAKE_CORE_NUMBER} +cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Release +# workaround to find ncurses on Nix () +sed -i 's/lcurses/lncurses/' build/node/CMakeFiles/kagome.dir/link.txt +cmake --build build --target kagome -j ${CMAKE_CORE_NUMBER} diff --git a/scripts/init.sh b/scripts/init.sh index a4a79d349c..449493ad7d 100755 --- a/scripts/init.sh +++ b/scripts/init.sh @@ -4,18 +4,30 @@ current_dir=$(dirname $(readlink -f "$0")) parent_dir=$(dirname "$current_dir") cd $parent_dir -set -a; source $current_dir/.env; set +a #include .env vars +#include .env vars and optionally .rust_env vars +set -a +if [ -z "$FLAKE_INITIATED" ]; then + source $current_dir/.rust_env +fi +source $current_dir/.env +set +a -apt update -apt install --no-install-recommends -y \ - build-essential git gcc ca-certificates python-is-python3 python3-pip \ - python3-venv curl libgmp-dev libncurses6 libnsl-dev libseccomp-dev +# if not using nix, install the following packages +if [ -z "$FLAKE_INITIATED" ]; then + apt update + apt install --no-install-recommends -y \ + build-essential git gcc ca-certificates python-is-python3 python3-pip \ + python3-venv curl libgmp-dev libncurses6 libnsl-dev libseccomp-dev +fi python3 -m venv "$parent_dir/venv" - echo "Python environment created successfully in $parent_dir/venv" -$parent_dir/venv/bin/pip install --no-cache-dir cmake==${CMAKE_VERSION} gitpython requests +if [ -z "$FLAKE_INITIATED" ]; then + $parent_dir/venv/bin/pip install --no-cache-dir cmake==${CMAKE_VERSION} gitpython requests -curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain ${RUST_VERSION} && \ - rustup default ${RUST_VERSION} + curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain ${RUST_VERSION} && + rustup default ${RUST_VERSION} +else + $parent_dir/venv/bin/pip install --no-cache-dir gitpython requests +fi diff --git a/test/core/application/chain_spec_test.cpp b/test/core/application/chain_spec_test.cpp index 17055f38d5..dad7c44739 100644 --- a/test/core/application/chain_spec_test.cpp +++ b/test/core/application/chain_spec_test.cpp @@ -16,8 +16,6 @@ using kagome::application::GenesisRawData; using kagome::common::Buffer; using kagome::crypto::Sr25519PublicKey; using libp2p::multi::Multiaddress; -using libp2p::peer::PeerId; -using libp2p::peer::PeerInfo; class ConfigurationStorageTest : public ::testing::Test { protected: diff --git a/test/core/network/sync_protocol_observer_test.cpp b/test/core/network/sync_protocol_observer_test.cpp index 5e6c9114a0..3fbf047ff1 100644 --- a/test/core/network/sync_protocol_observer_test.cpp +++ b/test/core/network/sync_protocol_observer_test.cpp @@ -15,7 +15,6 @@ #include "mock/core/blockchain/block_header_repository_mock.hpp" #include "mock/core/blockchain/block_tree_mock.hpp" #include "mock/core/network/beefy_mock.hpp" -#include "mock/core/network/peer_manager_mock.hpp" #include "mock/libp2p/host/host_mock.hpp" #include "primitives/block.hpp" #include "testutil/gmock_actions.hpp" @@ -45,9 +44,8 @@ class SyncProtocolObserverTest : public testing::Test { } void SetUp() override { - peer_manager_mock_ = std::make_shared(); - sync_protocol_observer_ = std::make_shared( - tree_, headers_, beefy_, peer_manager_mock_); + sync_protocol_observer_ = + std::make_shared(tree_, headers_, beefy_); } std::shared_ptr host_ = std::make_shared(); @@ -58,7 +56,6 @@ class SyncProtocolObserverTest : public testing::Test { std::make_shared(); std::shared_ptr sync_protocol_observer_; - std::shared_ptr peer_manager_mock_; std::shared_ptr beefy_ = std::make_shared(); const Hash256 block2_hash_ = "2"_hash256; @@ -101,7 +98,6 @@ TEST_F(SyncProtocolObserverTest, ProcessRequest) { .WillOnce(Return(::outcome::failure(boost::system::error_code{}))); EXPECT_CALL(*tree_, getBlockJustification(block4_hash_)) .WillOnce(Return(::outcome::failure(boost::system::error_code{}))); - EXPECT_CALL(*peer_manager_mock_, reserveStatusStreams(peer_info_.id)); EXPECT_CALL(*beefy_, getJustification(_)).WillRepeatedly([] { return ::outcome::success(std::nullopt); diff --git a/test/mock/core/network/peer_manager_mock.hpp b/test/mock/core/network/peer_manager_mock.hpp index ac4816a5f3..7a0341e73e 100644 --- a/test/mock/core/network/peer_manager_mock.hpp +++ b/test/mock/core/network/peer_manager_mock.hpp @@ -16,19 +16,10 @@ namespace kagome::network { public: MOCK_METHOD(void, connectToPeer, (const PeerInfo &), (override)); - MOCK_METHOD(void, reserveStreams, (const PeerId &), (const, override)); - - MOCK_METHOD(void, - reserveStatusStreams, - (const PeerId &), - (const, override)); - MOCK_METHOD(void, keepAlive, (const PeerId &), (override)); MOCK_METHOD(void, startPingingPeer, (const PeerId &), (override)); - MOCK_METHOD(std::shared_ptr, getStreamEngine, (), (override)); - MOCK_METHOD(AdvResult, retrieveCollatorData, (PeerState &, const primitives::BlockHash &), diff --git a/test/mock/core/network/router_mock.hpp b/test/mock/core/network/router_mock.hpp index e3186f376d..28f72c2d51 100644 --- a/test/mock/core/network/router_mock.hpp +++ b/test/mock/core/network/router_mock.hpp @@ -37,21 +37,11 @@ namespace kagome::network { (), (const, override)); - MOCK_METHOD(std::shared_ptr, - getValidationProtocolVStaging, - (), - (const, override)); - MOCK_METHOD(std::shared_ptr, getBlockAnnounceProtocol, (), (const, override)); - MOCK_METHOD(std::shared_ptr, - getCollationProtocolVStaging, - (), - (const, override)); - MOCK_METHOD(std::shared_ptr, getCollationProtocol, (), @@ -138,8 +128,6 @@ namespace kagome::network { // ON_CALL(*this, getPropagateTransactionsProtocol()).WillByDefault(testing::Return(propagate_transactions)); // ON_CALL(*this, getValidationProtocol()).WillByDefault(testing::Return(validation)); // ON_CALL(*this, getCollationProtocol()).WillByDefault(testing::Return(collation)); - // ON_CALL(*this, getCollationProtocolVStaging()).WillByDefault(testing::Return(collation_vstaging)); - // ON_CALL(*this, getValidationProtocolVStaging()).WillByDefault(testing::Return(validation_vstaging)); // ON_CALL(*this, getReqCollationProtocol()).WillByDefault(testing::Return(req_collation)); // ON_CALL(*this, getReqPovProtocol()).WillByDefault(testing::Return(req_pov)); fetch_chunk = std::make_shared(); diff --git a/test/mock/core/network/transactions_transmitter_mock.hpp b/test/mock/core/network/transactions_transmitter_mock.hpp index b9a51993bb..24b285cc7f 100644 --- a/test/mock/core/network/transactions_transmitter_mock.hpp +++ b/test/mock/core/network/transactions_transmitter_mock.hpp @@ -15,8 +15,8 @@ namespace kagome::network { class TransactionsTransmitterMock : public TransactionsTransmitter { public: MOCK_METHOD(void, - propagateTransactions, - (std::span), + propagateTransaction, + (primitives::Transaction), (override)); };