diff --git a/core/parachain/validator/impl/parachain_processor.cpp b/core/parachain/validator/impl/parachain_processor.cpp index d9cb35ad52..afe7e876f4 100644 --- a/core/parachain/validator/impl/parachain_processor.cpp +++ b/core/parachain/validator/impl/parachain_processor.cpp @@ -119,6 +119,8 @@ OUTCOME_CPP_DEFINE_CATEGORY(kagome::parachain, return "Not advertised"; case E::WRONG_PARA: return "Wrong para id"; + case E::THRESHOLD_LIMIT_REACHED: + return "Threshold reached"; } return "Unknown parachain processor error"; } @@ -1046,6 +1048,8 @@ namespace kagome::parachain { OUTCOME_TRY(session_info, parachain_host_->session_info(relay_parent, session_index)); OUTCOME_TRY(randomness, getBabeRandomness(block_header)); + OUTCOME_TRY(disabled_validators_, + parachain_host_->disabled_validators(relay_parent)); const auto &[validator_groups, group_rotation_info] = groups; if (!validator) { @@ -1214,6 +1218,14 @@ namespace kagome::parachain { seconding_limit, mode->max_candidate_depth); + std::unordered_set disabled_validators{ + disabled_validators_.begin(), disabled_validators_.end()}; + if (!disabled_validators.empty()) { + SL_TRACE(logger_, + "Disabled validators detected. (relay parent={})", + relay_parent); + } + SL_VERBOSE(logger_, "Inited new backing task v3.(assigned_para={}, " "assigned_core={}, our index={}, relay " @@ -1249,6 +1261,8 @@ namespace kagome::parachain { .issued_statements = {}, .peers_advertised = {}, .fallbacks = {}, + .backed_hashes = {}, + .disabled_validators = std::move(disabled_validators), .inject_core_index = inject_core_index, }; } @@ -1577,7 +1591,7 @@ namespace kagome::parachain { const libp2p::peer::PeerId &peer_id, const CandidateHash &candidate_hash, const RelayHash &relay_parent, - const ManifestSummary &manifest_summary, + ManifestSummary manifest_summary, ParachainId para_id, grid::ManifestKind manifest_kind) { auto peer_state = pm_->getPeerState(peer_id); @@ -1633,8 +1647,18 @@ namespace kagome::parachain { auto group_index = manifest_summary.claimed_group_index; auto claimed_parent_hash = manifest_summary.claimed_parent_hash; - /// TODO(iceseer): do `disabled validators` - /// https://github.com/qdrvm/kagome/issues/2060 + auto group = [&]() -> std::span { + if (auto g = + relay_parent_state->get().per_session_state->value().groups.get( + group_index)) { + return *g; + } + return {}; + }(); + + auto disabled_mask = relay_parent_state->get().disabled_bitmask(group); + manifest_summary.statement_knowledge.mask_seconded(disabled_mask); + manifest_summary.statement_knowledge.mask_valid(disabled_mask); BOOST_ASSERT(relay_parent_state->get().prospective_parachains_mode); const auto seconding_limit = @@ -2165,8 +2189,17 @@ namespace kagome::parachain { const auto &session_info = parachain_state->get().per_session_state->value().session_info; - CHECK_OR_RET(parachain_state->get().local_validator); + if (parachain_state->get().is_disabled(stm.compact.payload.ix)) { + SL_TRACE( + logger_, + "Ignoring a statement from disabled validator. (relay parent={}, " + "validator={})", + stm.relay_parent, + stm.compact.payload.ix); + return; + } + CHECK_OR_RET(parachain_state->get().local_validator); auto &local_validator = *parachain_state->get().local_validator; auto originator_group = parachain_state->get() @@ -2181,9 +2214,6 @@ namespace kagome::parachain { return; } - /// TODO(iceseer): do `disabled validators` - /// https://github.com/qdrvm/kagome/issues/2060 - auto &active = local_validator.active; auto cluster_sender_index = [&]() -> std::optional { std::span allowed_senders; @@ -2510,9 +2540,21 @@ namespace kagome::parachain { } } - /// TODO(iceseer): do `disabled validators` - /// Add disabled validators to the unwanted mask. - /// https://github.com/qdrvm/kagome/issues/2060 + auto disabled_mask = relay_parent_state.disabled_bitmask(*group); + if (disabled_mask.bits.size() + > unwanted_mask.seconded_in_group.bits.size()) { + unwanted_mask.seconded_in_group.bits.resize(disabled_mask.bits.size()); + } + if (disabled_mask.bits.size() + > unwanted_mask.validated_in_group.bits.size()) { + unwanted_mask.validated_in_group.bits.resize(disabled_mask.bits.size()); + } + for (size_t i = 0; i < disabled_mask.bits.size(); ++i) { + unwanted_mask.seconded_in_group.bits[i] = + unwanted_mask.seconded_in_group.bits[i] || disabled_mask.bits[i]; + unwanted_mask.validated_in_group.bits[i] = + unwanted_mask.validated_in_group.bits[i] || disabled_mask.bits[i]; + } auto backing_threshold = [&]() -> std::optional { auto bt = relay_parent_state.per_session_state->value() @@ -3193,16 +3235,48 @@ namespace kagome::parachain { init_with_not(and_mask.seconded_in_group, request.mask.seconded_in_group); init_with_not(and_mask.validated_in_group, request.mask.validated_in_group); - /// TODO(iceseer): do `disabled validators` check - /// https://github.com/qdrvm/kagome/issues/2060 std::vector> statements; + network::vstaging::StatementFilter sent_filter(group_size); relay_parent_state.get().statement_store->groupStatements( *group, request.candidate_hash, and_mask, [&](const IndexedAndSigned - &statement) { statements.emplace_back(statement); }); + &statement) { + for (size_t ix = 0; ix < group->size(); ++ix) { + if ((*group)[ix] == statement.payload.ix) { + visit_in_place( + getPayload(statement).inner_value, + [&](const network::vstaging::SecondedCandidateHash &) { + sent_filter.seconded_in_group.bits[ix] = true; + }, + [&](const network::vstaging::ValidCandidateHash &) { + sent_filter.validated_in_group.bits[ix] = true; + }, + [&](const auto &) {}); + } + } + statements.emplace_back(statement); + }); + + if (!is_cluster) { + auto threshold = std::get<1>(*groups.get_size_and_backing_threshold( + confirmed->get().group_index())); + const auto seconded_and_sufficient = + (sent_filter.has_seconded() + && sent_filter.backing_validators() >= threshold); + if (!seconded_and_sufficient) { + SL_INFO(logger_, + "Dropping a request from a grid peer because the backing " + "threshold is no longer met. (relay_parent={}, " + "candidate_hash={}, group_index={})", + confirmed->get().relay_parent(), + request.candidate_hash, + confirmed->get().group_index()); + return Error::THRESHOLD_LIMIT_REACHED; + } + } for (const auto &statement : statements) { if (is_cluster) { @@ -3329,7 +3403,7 @@ namespace kagome::parachain { .from_validator = statement.payload.ix, .backing = {}}; - auto const &[it, _] = fallbacks.insert( + const auto &[it, _] = fallbacks.insert( std::make_pair(candidate_hash, std::move(attesting))); return it->second; }, diff --git a/core/parachain/validator/parachain_processor.hpp b/core/parachain/validator/parachain_processor.hpp index 21fe80207f..41267238b3 100644 --- a/core/parachain/validator/parachain_processor.hpp +++ b/core/parachain/validator/parachain_processor.hpp @@ -132,7 +132,8 @@ namespace kagome::parachain { NO_PEER, ALREADY_REQUESTED, NOT_ADVERTISED, - WRONG_PARA + WRONG_PARA, + THRESHOLD_LIMIT_REACHED, }; static constexpr uint64_t kBackgroundWorkers = 5; @@ -447,9 +448,24 @@ namespace kagome::parachain { std::unordered_set issued_statements; std::unordered_set peers_advertised; std::unordered_map fallbacks; - std::unordered_set backed_hashes{}; + std::unordered_set backed_hashes; + std::unordered_set disabled_validators; bool inject_core_index; + + bool is_disabled(ValidatorIndex validator_index) const { + return disabled_validators.contains(validator_index); + } + + scale::BitVec disabled_bitmask( + const std::span &group) const { + scale::BitVec v; + v.bits.resize(group.size()); + for (size_t ix = 0; ix < group.size(); ++ix) { + v.bits[ix] = is_disabled(group[ix]); + } + return v; + } }; /** @@ -658,7 +674,7 @@ namespace kagome::parachain { const libp2p::peer::PeerId &peer_id, const CandidateHash &candidate_hash, const RelayHash &relay_parent, - const ManifestSummary &manifest_summary, + ManifestSummary manifest_summary, ParachainId para_id, grid::ManifestKind manifest_kind); network::vstaging::StatementFilter local_knowledge_filter(