From d429d2ffc13cffa685f9decc2c168ab9d5815333 Mon Sep 17 00:00:00 2001 From: Boris Erakhtin Date: Thu, 20 Feb 2025 13:24:56 +0500 Subject: [PATCH 1/3] Metrics of implicit, explicit and missed parachain votes --- .../validator/impl/parachain_processor.cpp | 311 +++++++++++++++++- .../validator/parachain_processor.hpp | 27 +- 2 files changed, 334 insertions(+), 4 deletions(-) diff --git a/core/parachain/validator/impl/parachain_processor.cpp b/core/parachain/validator/impl/parachain_processor.cpp index 8154de3704..6ae29d022e 100644 --- a/core/parachain/validator/impl/parachain_processor.cpp +++ b/core/parachain/validator/impl/parachain_processor.cpp @@ -127,7 +127,14 @@ OUTCOME_CPP_DEFINE_CATEGORY(kagome::parachain, namespace { constexpr const char *kIsParachainValidator = "kagome_node_is_parachain_validator"; -} + constexpr auto kImplicitVotes = "kagome_parachain_implicit_votes"; + constexpr auto kExplicitVotes = "kagome_parachain_explicit_votes"; + constexpr auto kNoVotes = "kagome_parachain_no_votes"; + constexpr auto kSessionIndex = "kagome_session_index"; + constexpr auto parachain_inherent_data_extrinsic_version = 0x04; + constexpr auto parachain_inherent_data_call = 0x36; + constexpr auto parachain_inherent_data_module = 0x00; +} // namespace namespace kagome::parachain { @@ -233,6 +240,24 @@ namespace kagome::parachain { metric_kagome_parachain_candidate_backing_candidates_seconded_total_ = metrics_registry_->registerCounterMetric( "kagome_parachain_candidate_backing_candidates_seconded_total"); + + metrics_registry_->registerGaugeFamily(kSessionIndex, + "Parachain session index"); + metric_session_index_ = + metrics_registry_->registerGaugeMetric(kSessionIndex); + metric_session_index_->set(0); + metrics_registry_->registerCounterFamily( + kImplicitVotes, "Implicit votes for parachain candidates"); + metric_kagome_parachain_candidate_implicit_votes_total_ = + metrics_registry_->registerCounterMetric(kImplicitVotes); + metrics_registry_->registerCounterFamily( + kExplicitVotes, "Explicit votes for parachain candidates"); + metric_kagome_parachain_candidate_explicit_votes_total_ = + metrics_registry_->registerCounterMetric(kExplicitVotes); + metrics_registry_->registerCounterFamily( + kNoVotes, "No votes for parachain candidates"); + metric_kagome_parachain_candidate_no_votes_total_ = + metrics_registry_->registerCounterMetric(kNoVotes); } void ParachainProcessorImpl::OnBroadcastBitfields( @@ -286,6 +311,12 @@ namespace kagome::parachain { self->onViewUpdated(event); }); + chain_sub_.onFinalize([WEAK_SELF] { + WEAK_LOCK(self); + if (self) { + self->onFinalize(); + } + }); return true; } @@ -351,7 +382,20 @@ namespace kagome::parachain { mode ? std::move(pruned_h) : std::vector{removed}; for (const auto &removed : pruned) { - our_current_state_.state_by_relay_parent.erase(removed); + auto it = our_current_state_.state_by_relay_parent.find(removed); + if (it != our_current_state_.state_by_relay_parent.end()) { + const auto &relay_parent = it->first; + state_by_relay_parent_to_check_[relay_parent] = std::move(it->second); + if (auto block_number = block_tree_->getNumberByHash(relay_parent)) { + relay_parent_depth_[relay_parent] = block_number.value(); + } else { + SL_DEBUG(logger_, + "Failed to get block number while pruning relay parent " + "state. (relay_parent={})", + relay_parent); + } + our_current_state_.state_by_relay_parent.erase(it); + } { /// remove cancelations auto &container = @@ -667,7 +711,10 @@ namespace kagome::parachain { if (remaining.contains(it->first)) { ++it; } else { + const auto &relay_parent = it->first; _keeper_.emplace_back(it->second.per_session_state); + state_by_relay_parent_to_check_[relay_parent] = std::move(it->second); + relay_parent_depth_[relay_parent] = block_header.number; it = our_current_state_.state_by_relay_parent.erase(it); } } @@ -3272,4 +3319,264 @@ namespace kagome::parachain { return outcome::success(); } + void ParachainProcessorImpl::onFinalize() { + BOOST_ASSERT(main_pool_handler_->isInCurrentThread()); + if (not isValidatingNode()) { + return; + } + if (state_by_relay_parent_to_check_.empty()) { + return; + } + const auto last_finalized_block = block_tree_->getLastFinalized().number; + static std::optional + previous_last_finalized_block = std::nullopt; + primitives::BlockNumber current_block_number = 0; + if (not previous_last_finalized_block) { + previous_last_finalized_block = last_finalized_block; + if (last_finalized_block == 0) { + return; + } + } else { + current_block_number = previous_last_finalized_block.value() + 1; + } + for (auto i = current_block_number - 1; i < last_finalized_block; ++i) { + const auto block_hash_res = block_tree_->getBlockHash(i); + if (not block_hash_res) { + SL_DEBUG(logger_, + "Error {} getting block hash for block number {}", + block_hash_res.error(), + i); + continue; + } + const auto &block_hash_opt = block_hash_res.value(); + if (not block_hash_opt) { + continue; + } + const auto &block_hash = block_hash_opt.value(); + const auto session_index = + parachain_host_->session_index_for_child(block_hash); + if (not session_index) { + SL_DEBUG(logger_, + "Error {} getting session index for block {}", + session_index.error(), + block_hash); + } + metric_session_index_->set(session_index.value()); + proceedVotesOnRelayParent(block_hash); + } + previous_last_finalized_block = last_finalized_block; + for (auto it = relay_parent_depth_.begin(); + it != relay_parent_depth_.end();) { + if (it->second < last_finalized_block) { + const auto &relay_parent = it->first; + const auto jit = state_by_relay_parent_to_check_.find(relay_parent); + if (jit != state_by_relay_parent_to_check_.end()) { + proceedVotesOnRelayParent(relay_parent); + } + it = relay_parent_depth_.erase(it); + } else { + ++it; + } + } + } + + void ParachainProcessorImpl::proceedVotesOnRelayParent( + const primitives::BlockHash &block_hash) { + const auto it = state_by_relay_parent_to_check_.find(block_hash); + if (it == state_by_relay_parent_to_check_.end()) { + return; + } + + struct CleanupGuard { + std::function cleanup; + ~CleanupGuard() { + cleanup(); + } + } cleanup_guard{[this, &block_hash] { + state_by_relay_parent_to_check_.erase(block_hash); + }}; + + const auto ¶chain_state = it->second; + + if (not parachain_state.assigned_core) { + return; + } + + const auto assigned_core = parachain_state.assigned_core.value(); + const auto group_it = parachain_state.table_context.groups.find(assigned_core); + if (group_it == parachain_state.table_context.groups.end()) { + return; + } + + const auto validator_index_res = + utils::map(parachain_state.table_context.validator, + [](const auto &signer) { return signer.validatorIndex(); }); + if (not validator_index_res) { + return; + } + + const auto validator_index = validator_index_res.value(); + const auto &group = group_it->second; + std::unordered_map group_validator_position; + + for (std::size_t pos = 0; pos < group.size(); ++pos) { + group_validator_position.emplace(group[pos], pos); + } + + const auto validator_position_it = group_validator_position.find(validator_index); + if (validator_position_it == group_validator_position.end()) { + return; + } + + const auto validator_position = validator_position_it->second; + + const auto availability_cores_res = + parachain_host_->availability_cores(block_hash); + if (not availability_cores_res) { + SL_DEBUG(logger_, + "Availability cores error {} on relay parent {}", + availability_cores_res.error(), + block_hash); + return; + } + + const auto &availability_cores = availability_cores_res.value(); + if (assigned_core >= availability_cores.size()) { + return; + } + + const auto parachain_id_opt = extractParachainId(availability_cores[assigned_core]); + if (not parachain_id_opt) { + return; + } + const auto ¶chain_id = parachain_id_opt.value(); + const auto candidate_res = parachain_host_->candidate_pending_availability( + block_hash, parachain_id); + if (not candidate_res) { + SL_DEBUG(logger_, + "Candidate pending availability error {} on relay parent {}", + candidate_res.error(), + block_hash); + return; + } + const auto &candidate_opt = candidate_res.value(); + if (not candidate_opt) { + return; + } + + const auto block_body_res = block_tree_->getBlockBody(block_hash); + if (not block_body_res) { + SL_DEBUG(logger_, + "Block body error {} for block {}", + block_body_res.error(), + block_hash); + return; + } + + const auto &block_body = block_body_res.value(); + const auto parachain_inherent_data = extractParachainInherentData(block_body); + if (not parachain_inherent_data) { + return; + } + + bool explicit_found = false, implicit_found = false; + checkCandidateVotes(parachain_inherent_data.value(), + candidate_opt.value(), + validator_position, + explicit_found, + implicit_found); + + if (explicit_found) { + SL_TRACE(logger_, + "Explicit vote found for parachain {} on relay parent {}", + parachain_id, + block_hash); + metric_kagome_parachain_candidate_explicit_votes_total_->inc(); + } else if (implicit_found) { + SL_TRACE(logger_, + "Implicit vote found for parachain {} on relay parent {}", + parachain_id, + block_hash); + metric_kagome_parachain_candidate_implicit_votes_total_->inc(); + } else { + SL_TRACE(logger_, + "No vote found for parachain {} on relay parent {}", + parachain_id, + block_hash); + metric_kagome_parachain_candidate_no_votes_total_->inc(); + } + } + + std::optional ParachainProcessorImpl::extractParachainId( + const runtime::CoreState &core) const { + if (auto occupied_core = std::get_if(&core)) { + return occupied_core->candidate_descriptor.para_id; + } + return std::nullopt; + } + + std::optional + ParachainProcessorImpl::extractParachainInherentData( + const std::vector &block_body) const { + for (const auto &extrinsic : block_body) { + if (extrinsic.data.size() < 3 + || extrinsic.data[0] != parachain_inherent_data_extrinsic_version + || extrinsic.data[1] != parachain_inherent_data_call + || extrinsic.data[2] != parachain_inherent_data_module) { + continue; + } + + std::vector buffer(extrinsic.data.begin() + 3, + extrinsic.data.end()); + auto decode_res = scale::decode(buffer); + + if (decode_res) { + return decode_res.value(); + } + + SL_DEBUG(logger_, + "Failed to decode ParachainInherentData: {}", + decode_res.error()); + } + + return std::nullopt; + } + + void ParachainProcessorImpl::checkCandidateVotes( + const parachain::ParachainInherentData ¶chain_inherent_data, + const runtime::CommittedCandidateReceipt &candidate, + std::size_t validator_position, + bool &explicit_found, + bool &implicit_found) const { + for (const auto &backed_candidate : + parachain_inherent_data.backed_candidates) { + if (backed_candidate.candidate != candidate) { + continue; + } + + if (backed_candidate.validator_indices.bits.size() <= validator_position + || backed_candidate.validity_votes.size() <= validator_position + || not backed_candidate.validator_indices.bits[validator_position]) { + return; + } + + boost::apply_visitor( + [&](const auto &attestation) { + using T = std::decay_t; + if constexpr (std::is_same_v< + T, + network::ValidityAttestation::Implicit>) { + implicit_found = true; + } else if constexpr (std::is_same_v< + T, + network::ValidityAttestation::Explicit>) { + explicit_found = true; + } + }, + backed_candidate.validity_votes[validator_position].kind); + + break; + } + } + } // namespace kagome::parachain diff --git a/core/parachain/validator/parachain_processor.hpp b/core/parachain/validator/parachain_processor.hpp index bfd73150e2..9150524abb 100644 --- a/core/parachain/validator/parachain_processor.hpp +++ b/core/parachain/validator/parachain_processor.hpp @@ -32,6 +32,7 @@ #include "parachain/availability/store/store.hpp" #include "parachain/backing/cluster.hpp" #include "parachain/backing/store.hpp" +#include "parachain/parachain_inherent_data.hpp" #include "parachain/pvf/precheck.hpp" #include "parachain/pvf/pvf.hpp" #include "parachain/validator/backed_candidates_source.hpp" @@ -666,6 +667,7 @@ namespace kagome::parachain { const RelayHash &relay_parent, const SignedFullStatementWithPVD &statement); + void onFinalize(); void handle_active_leaves_update_for_validator(const network::ExView &event, std::vector pruned); void onViewUpdated(const network::ExView &event); @@ -761,6 +763,18 @@ namespace kagome::parachain { const primitives::BlockHash &candidate_hash, const network::SignedStatement &statement); + void proceedVotesOnRelayParent(const primitives::BlockHash &block_hash); + std::optional extractParachainId( + const runtime::CoreState &core) const; + std::optional + extractParachainInherentData( + const std::vector &block_body) const; + void checkCandidateVotes( + const parachain::ParachainInherentData ¶chain_inherent_data, + const runtime::CommittedCandidateReceipt &candidate, + std::size_t validator_position, + bool &explicit_found, + bool &implicit_found) const; std::shared_ptr pm_; std::shared_ptr runtime_info_; std::shared_ptr crypto_provider_; @@ -788,6 +802,9 @@ namespace kagome::parachain { blocked_from_seconding; } validator_side; } our_current_state_; + std::unordered_map + state_by_relay_parent_to_check_; + std::unordered_map relay_parent_depth_; std::shared_ptr main_pool_handler_; std::shared_ptr hasher_; @@ -821,8 +838,14 @@ namespace kagome::parachain { metrics::RegistryPtr metrics_registry_ = metrics::createRegistry(); metrics::Gauge *metric_is_parachain_validator_; - metrics::Counter *metric_kagome_parachain_candidate_backing_signed_statements_total_; - metrics::Counter *metric_kagome_parachain_candidate_backing_candidates_seconded_total_; + metrics::Counter + *metric_kagome_parachain_candidate_backing_signed_statements_total_; + metrics::Counter + *metric_kagome_parachain_candidate_backing_candidates_seconded_total_; + metrics::Gauge *metric_session_index_; + metrics::Counter *metric_kagome_parachain_candidate_implicit_votes_total_; + metrics::Counter *metric_kagome_parachain_candidate_explicit_votes_total_; + metrics::Counter *metric_kagome_parachain_candidate_no_votes_total_; }; } // namespace kagome::parachain From 9070d99d8a89efbd5f589e7de793f44463696b80 Mon Sep 17 00:00:00 2001 From: Boris Erakhtin Date: Fri, 21 Feb 2025 12:37:58 +0500 Subject: [PATCH 2/3] pr_update: availability_cores is taken from internal structure --- .../validator/impl/parachain_processor.cpp | 43 ++++++++----------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/core/parachain/validator/impl/parachain_processor.cpp b/core/parachain/validator/impl/parachain_processor.cpp index 6ae29d022e..c67e5feb20 100644 --- a/core/parachain/validator/impl/parachain_processor.cpp +++ b/core/parachain/validator/impl/parachain_processor.cpp @@ -3403,7 +3403,8 @@ namespace kagome::parachain { } const auto assigned_core = parachain_state.assigned_core.value(); - const auto group_it = parachain_state.table_context.groups.find(assigned_core); + const auto group_it = + parachain_state.table_context.groups.find(assigned_core); if (group_it == parachain_state.table_context.groups.end()) { return; } @@ -3423,29 +3424,20 @@ namespace kagome::parachain { group_validator_position.emplace(group[pos], pos); } - const auto validator_position_it = group_validator_position.find(validator_index); + const auto validator_position_it = + group_validator_position.find(validator_index); if (validator_position_it == group_validator_position.end()) { return; } const auto validator_position = validator_position_it->second; - - const auto availability_cores_res = - parachain_host_->availability_cores(block_hash); - if (not availability_cores_res) { - SL_DEBUG(logger_, - "Availability cores error {} on relay parent {}", - availability_cores_res.error(), - block_hash); - return; - } - - const auto &availability_cores = availability_cores_res.value(); + const auto &availability_cores = parachain_state.availability_cores; if (assigned_core >= availability_cores.size()) { return; } - const auto parachain_id_opt = extractParachainId(availability_cores[assigned_core]); + const auto parachain_id_opt = + extractParachainId(availability_cores[assigned_core]); if (not parachain_id_opt) { return; } @@ -3474,7 +3466,8 @@ namespace kagome::parachain { } const auto &block_body = block_body_res.value(); - const auto parachain_inherent_data = extractParachainInherentData(block_body); + const auto parachain_inherent_data = + extractParachainInherentData(block_body); if (not parachain_inherent_data) { return; } @@ -3488,21 +3481,21 @@ namespace kagome::parachain { if (explicit_found) { SL_TRACE(logger_, - "Explicit vote found for parachain {} on relay parent {}", - parachain_id, - block_hash); + "Explicit vote found for parachain {} on relay parent {}", + parachain_id, + block_hash); metric_kagome_parachain_candidate_explicit_votes_total_->inc(); } else if (implicit_found) { SL_TRACE(logger_, - "Implicit vote found for parachain {} on relay parent {}", - parachain_id, - block_hash); + "Implicit vote found for parachain {} on relay parent {}", + parachain_id, + block_hash); metric_kagome_parachain_candidate_implicit_votes_total_->inc(); } else { SL_TRACE(logger_, - "No vote found for parachain {} on relay parent {}", - parachain_id, - block_hash); + "No vote found for parachain {} on relay parent {}", + parachain_id, + block_hash); metric_kagome_parachain_candidate_no_votes_total_->inc(); } } From 44d89fbff29694976be2f8c7809fe246ec2ac5ae Mon Sep 17 00:00:00 2001 From: Boris Erakhtin Date: Fri, 21 Feb 2025 13:06:44 +0500 Subject: [PATCH 3/3] pr_update: fix of cleanup guard by comment and clang tidy --- .../validator/impl/parachain_processor.cpp | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/core/parachain/validator/impl/parachain_processor.cpp b/core/parachain/validator/impl/parachain_processor.cpp index c67e5feb20..927450aab6 100644 --- a/core/parachain/validator/impl/parachain_processor.cpp +++ b/core/parachain/validator/impl/parachain_processor.cpp @@ -3387,14 +3387,10 @@ namespace kagome::parachain { return; } - struct CleanupGuard { - std::function cleanup; - ~CleanupGuard() { - cleanup(); - } - } cleanup_guard{[this, &block_hash] { - state_by_relay_parent_to_check_.erase(block_hash); - }}; + auto cleanup_guard = std::unique_ptr>( + new int, [this, &block_hash](void *) { + state_by_relay_parent_to_check_.erase(block_hash); + }); const auto ¶chain_state = it->second; @@ -3481,21 +3477,21 @@ namespace kagome::parachain { if (explicit_found) { SL_TRACE(logger_, - "Explicit vote found for parachain {} on relay parent {}", - parachain_id, - block_hash); + "Explicit vote found for parachain {} on relay parent {}", + parachain_id, + block_hash); metric_kagome_parachain_candidate_explicit_votes_total_->inc(); } else if (implicit_found) { SL_TRACE(logger_, - "Implicit vote found for parachain {} on relay parent {}", - parachain_id, - block_hash); + "Implicit vote found for parachain {} on relay parent {}", + parachain_id, + block_hash); metric_kagome_parachain_candidate_implicit_votes_total_->inc(); } else { SL_TRACE(logger_, - "No vote found for parachain {} on relay parent {}", - parachain_id, - block_hash); + "No vote found for parachain {} on relay parent {}", + parachain_id, + block_hash); metric_kagome_parachain_candidate_no_votes_total_->inc(); } }