Unverified Commit 71e76705 authored by Bernhard Schuster's avatar Bernhard Schuster Committed by GitHub
Browse files

remove provisioner checks (#4254)



* chore/provisioner: move metrics to a separate module

* avoid the duplicate names

* reduce all checks

* fixup tests

* Update node/core/provisioner/src/lib.rs
Co-authored-by: Zeke Mostov's avatarZeke Mostov <z.mostov@gmail.com>

* chore: fmt

* chore: spellcheck

* doc

* remove the enum anti-pattern

* guide update - remove all the responsibilities

* add another trivial check

* Update node/core/provisioner/src/metrics.rs
Co-authored-by: Andronik Ordian's avatarAndronik Ordian <write@reusable.software>

* Update roadmap/implementers-guide/src/node/utility/provisioner.md
Co-authored-by: Andronik Ordian's avatarAndronik Ordian <write@reusable.software>

* Update node/core/provisioner/src/metrics.rs
Co-authored-by: Andronik Ordian's avatarAndronik Ordian <write@reusable.software>
Co-authored-by: Zeke Mostov's avatarZeke Mostov <z.mostov@gmail.com>
Co-authored-by: Andronik Ordian's avatarAndronik Ordian <write@reusable.software>
parent 1ec4819c
Pipeline #167026 canceled with stages
in 14 minutes and 40 seconds
...@@ -567,9 +567,9 @@ dependencies = [ ...@@ -567,9 +567,9 @@ dependencies = [
[[package]] [[package]]
name = "bitvec" name = "bitvec"
version = "0.20.1" version = "0.20.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5011ffc90248764d7005b0e10c7294f5aa1bd87d9dd7248f4ad475b347c294d" checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848"
dependencies = [ dependencies = [
"funty", "funty",
"radium 0.6.2", "radium 0.6.2",
...@@ -729,7 +729,7 @@ dependencies = [ ...@@ -729,7 +729,7 @@ dependencies = [
name = "bp-messages" name = "bp-messages"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bitvec 0.20.1", "bitvec 0.20.4",
"bp-runtime", "bp-runtime",
"frame-support", "frame-support",
"frame-system", "frame-system",
...@@ -3159,7 +3159,7 @@ name = "kusama-runtime" ...@@ -3159,7 +3159,7 @@ name = "kusama-runtime"
version = "0.9.13" version = "0.9.13"
dependencies = [ dependencies = [
"beefy-primitives", "beefy-primitives",
"bitvec 0.20.1", "bitvec 0.20.4",
"frame-benchmarking", "frame-benchmarking",
"frame-election-provider-support", "frame-election-provider-support",
"frame-executive", "frame-executive",
...@@ -4884,7 +4884,7 @@ dependencies = [ ...@@ -4884,7 +4884,7 @@ dependencies = [
name = "pallet-bridge-messages" name = "pallet-bridge-messages"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bitvec 0.20.1", "bitvec 0.20.4",
"bp-message-dispatch", "bp-message-dispatch",
"bp-messages", "bp-messages",
"bp-rialto", "bp-rialto",
...@@ -5565,7 +5565,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" ...@@ -5565,7 +5565,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909"
dependencies = [ dependencies = [
"arrayvec 0.7.2", "arrayvec 0.7.2",
"bitvec 0.20.1", "bitvec 0.20.4",
"byte-slice-cast", "byte-slice-cast",
"impl-trait-for-tuples", "impl-trait-for-tuples",
"parity-scale-codec-derive", "parity-scale-codec-derive",
...@@ -5912,7 +5912,7 @@ name = "polkadot-availability-bitfield-distribution" ...@@ -5912,7 +5912,7 @@ name = "polkadot-availability-bitfield-distribution"
version = "0.9.13" version = "0.9.13"
dependencies = [ dependencies = [
"assert_matches", "assert_matches",
"bitvec 0.20.1", "bitvec 0.20.4",
"env_logger 0.9.0", "env_logger 0.9.0",
"futures 0.3.17", "futures 0.3.17",
"log", "log",
...@@ -6186,7 +6186,7 @@ name = "polkadot-node-core-approval-voting" ...@@ -6186,7 +6186,7 @@ name = "polkadot-node-core-approval-voting"
version = "0.9.13" version = "0.9.13"
dependencies = [ dependencies = [
"assert_matches", "assert_matches",
"bitvec 0.20.1", "bitvec 0.20.4",
"derive_more", "derive_more",
"futures 0.3.17", "futures 0.3.17",
"futures-timer 3.0.2", "futures-timer 3.0.2",
...@@ -6222,7 +6222,7 @@ name = "polkadot-node-core-av-store" ...@@ -6222,7 +6222,7 @@ name = "polkadot-node-core-av-store"
version = "0.9.13" version = "0.9.13"
dependencies = [ dependencies = [
"assert_matches", "assert_matches",
"bitvec 0.20.1", "bitvec 0.20.4",
"env_logger 0.9.0", "env_logger 0.9.0",
"futures 0.3.17", "futures 0.3.17",
"futures-timer 3.0.2", "futures-timer 3.0.2",
...@@ -6249,7 +6249,7 @@ name = "polkadot-node-core-backing" ...@@ -6249,7 +6249,7 @@ name = "polkadot-node-core-backing"
version = "0.9.13" version = "0.9.13"
dependencies = [ dependencies = [
"assert_matches", "assert_matches",
"bitvec 0.20.1", "bitvec 0.20.4",
"futures 0.3.17", "futures 0.3.17",
"polkadot-erasure-coding", "polkadot-erasure-coding",
"polkadot-node-primitives", "polkadot-node-primitives",
...@@ -6349,7 +6349,7 @@ name = "polkadot-node-core-dispute-coordinator" ...@@ -6349,7 +6349,7 @@ name = "polkadot-node-core-dispute-coordinator"
version = "0.9.13" version = "0.9.13"
dependencies = [ dependencies = [
"assert_matches", "assert_matches",
"bitvec 0.20.1", "bitvec 0.20.4",
"derive_more", "derive_more",
"futures 0.3.17", "futures 0.3.17",
"kvdb", "kvdb",
...@@ -6388,15 +6388,13 @@ dependencies = [ ...@@ -6388,15 +6388,13 @@ dependencies = [
name = "polkadot-node-core-provisioner" name = "polkadot-node-core-provisioner"
version = "0.9.13" version = "0.9.13"
dependencies = [ dependencies = [
"bitvec 0.20.1", "bitvec 0.20.4",
"futures 0.3.17", "futures 0.3.17",
"futures-timer 3.0.2", "futures-timer 3.0.2",
"polkadot-node-subsystem", "polkadot-node-subsystem",
"polkadot-node-subsystem-test-helpers", "polkadot-node-subsystem-test-helpers",
"polkadot-node-subsystem-util", "polkadot-node-subsystem-util",
"polkadot-primitives", "polkadot-primitives",
"sp-application-crypto",
"sp-keystore",
"thiserror", "thiserror",
"tracing", "tracing",
] ]
...@@ -6669,7 +6667,7 @@ dependencies = [ ...@@ -6669,7 +6667,7 @@ dependencies = [
name = "polkadot-primitives" name = "polkadot-primitives"
version = "0.9.13" version = "0.9.13"
dependencies = [ dependencies = [
"bitvec 0.20.1", "bitvec 0.20.4",
"frame-system", "frame-system",
"hex-literal", "hex-literal",
"parity-scale-codec", "parity-scale-codec",
...@@ -6729,7 +6727,7 @@ name = "polkadot-runtime" ...@@ -6729,7 +6727,7 @@ name = "polkadot-runtime"
version = "0.9.13" version = "0.9.13"
dependencies = [ dependencies = [
"beefy-primitives", "beefy-primitives",
"bitvec 0.20.1", "bitvec 0.20.4",
"frame-benchmarking", "frame-benchmarking",
"frame-election-provider-support", "frame-election-provider-support",
"frame-executive", "frame-executive",
...@@ -6812,7 +6810,7 @@ name = "polkadot-runtime-common" ...@@ -6812,7 +6810,7 @@ name = "polkadot-runtime-common"
version = "0.9.13" version = "0.9.13"
dependencies = [ dependencies = [
"beefy-primitives", "beefy-primitives",
"bitvec 0.20.1", "bitvec 0.20.4",
"frame-benchmarking", "frame-benchmarking",
"frame-election-provider-support", "frame-election-provider-support",
"frame-support", "frame-support",
...@@ -6862,7 +6860,7 @@ name = "polkadot-runtime-parachains" ...@@ -6862,7 +6860,7 @@ name = "polkadot-runtime-parachains"
version = "0.9.13" version = "0.9.13"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"bitvec 0.20.1", "bitvec 0.20.4",
"derive_more", "derive_more",
"frame-benchmarking", "frame-benchmarking",
"frame-support", "frame-support",
...@@ -7144,7 +7142,7 @@ name = "polkadot-test-runtime" ...@@ -7144,7 +7142,7 @@ name = "polkadot-test-runtime"
version = "0.9.13" version = "0.9.13"
dependencies = [ dependencies = [
"beefy-primitives", "beefy-primitives",
"bitvec 0.20.1", "bitvec 0.20.4",
"frame-election-provider-support", "frame-election-provider-support",
"frame-executive", "frame-executive",
"frame-support", "frame-support",
...@@ -9106,7 +9104,7 @@ version = "1.0.0" ...@@ -9106,7 +9104,7 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c55b744399c25532d63a0d2789b109df8d46fc93752d46b0782991a931a782f" checksum = "5c55b744399c25532d63a0d2789b109df8d46fc93752d46b0782991a931a782f"
dependencies = [ dependencies = [
"bitvec 0.20.1", "bitvec 0.20.4",
"cfg-if 1.0.0", "cfg-if 1.0.0",
"derive_more", "derive_more",
"parity-scale-codec", "parity-scale-codec",
...@@ -11666,7 +11664,7 @@ name = "westend-runtime" ...@@ -11666,7 +11664,7 @@ name = "westend-runtime"
version = "0.9.13" version = "0.9.13"
dependencies = [ dependencies = [
"beefy-primitives", "beefy-primitives",
"bitvec 0.20.1", "bitvec 0.20.4",
"frame-benchmarking", "frame-benchmarking",
"frame-election-provider-support", "frame-election-provider-support",
"frame-executive", "frame-executive",
......
...@@ -5,7 +5,6 @@ authors = ["Parity Technologies <admin@parity.io>"] ...@@ -5,7 +5,6 @@ authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
bitvec = { version = "0.20.1", default-features = false, features = ["alloc"] }
futures = "0.3.17" futures = "0.3.17"
tracing = "0.1.29" tracing = "0.1.29"
thiserror = "1.0.30" thiserror = "1.0.30"
...@@ -15,6 +14,5 @@ polkadot-node-subsystem-util = { path = "../../subsystem-util" } ...@@ -15,6 +14,5 @@ polkadot-node-subsystem-util = { path = "../../subsystem-util" }
futures-timer = "3.0.2" futures-timer = "3.0.2"
[dev-dependencies] [dev-dependencies]
sp-application-crypto = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" }
polkadot-node-subsystem-test-helpers = { path = "../../subsystem-test-helpers" } polkadot-node-subsystem-test-helpers = { path = "../../subsystem-test-helpers" }
bitvec = { version = "0.20.1", default-features = false, features = [] }
...@@ -14,12 +14,11 @@ ...@@ -14,12 +14,11 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>. // along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! The provisioner is responsible for assembling a relay chain block //! The provisioner is responsible for assembling a set of items, from which the
//! from a set of available parachain candidates of its choice. //! runtime will pick a subset and create a relay chain block.
#![deny(missing_docs, unused_crate_dependencies)] #![deny(missing_docs, unused_crate_dependencies)]
use bitvec::vec::BitVec;
use futures::{ use futures::{
channel::{mpsc, oneshot}, channel::{mpsc, oneshot},
prelude::*, prelude::*,
...@@ -29,25 +28,24 @@ use polkadot_node_subsystem::{ ...@@ -29,25 +28,24 @@ use polkadot_node_subsystem::{
errors::{ChainApiError, RuntimeApiError}, errors::{ChainApiError, RuntimeApiError},
jaeger, jaeger,
messages::{ messages::{
CandidateBackingMessage, ChainApiMessage, DisputeCoordinatorMessage, ProvisionableData, CandidateBackingMessage, DisputeCoordinatorMessage, ProvisionableData,
ProvisionerInherentData, ProvisionerMessage, ProvisionerInherentData, ProvisionerMessage,
}, },
PerLeafSpan, SubsystemSender, PerLeafSpan, SubsystemSender,
}; };
use polkadot_node_subsystem_util::{ use polkadot_node_subsystem_util::{self as util, JobSender, JobSubsystem, JobTrait};
self as util,
metrics::{self, prometheus},
request_availability_cores, request_persisted_validation_data, JobSender, JobSubsystem,
JobTrait,
};
use polkadot_primitives::v1::{ use polkadot_primitives::v1::{
BackedCandidate, BlockNumber, CandidateReceipt, CoreState, DisputeStatement, BackedCandidate, CandidateHash, CandidateReceipt, DisputeStatement, DisputeStatementSet, Hash,
DisputeStatementSet, Hash, MultiDisputeStatementSet, OccupiedCoreAssumption, Id as ParaId, MultiDisputeStatementSet, SignedAvailabilityBitfield,
SignedAvailabilityBitfield, ValidatorIndex, SignedAvailabilityBitfields,
}; };
use std::{collections::BTreeMap, pin::Pin, sync::Arc}; use std::{collections::HashSet, pin::Pin, sync::Arc};
use thiserror::Error; use thiserror::Error;
mod metrics;
pub use self::metrics::*;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
...@@ -106,40 +104,17 @@ pub enum Error { ...@@ -106,40 +104,17 @@ pub enum Error {
#[error(transparent)] #[error(transparent)]
Util(#[from] util::Error), Util(#[from] util::Error),
#[error("failed to get availability cores")]
CanceledAvailabilityCores(#[source] oneshot::Canceled),
#[error("failed to get persisted validation data")]
CanceledPersistedValidationData(#[source] oneshot::Canceled),
#[error("failed to get block number")]
CanceledBlockNumber(#[source] oneshot::Canceled),
#[error("failed to get backed candidates")] #[error("failed to get backed candidates")]
CanceledBackedCandidates(#[source] oneshot::Canceled), CanceledBackedCandidates(#[source] oneshot::Canceled),
#[error("failed to get votes on dispute")]
CanceledCandidateVotes(#[source] oneshot::Canceled),
#[error(transparent)] #[error(transparent)]
ChainApi(#[from] ChainApiError), ChainApi(#[from] ChainApiError),
#[error(transparent)] #[error(transparent)]
Runtime(#[from] RuntimeApiError), Runtime(#[from] RuntimeApiError),
#[error("failed to send message to ChainAPI")]
ChainApiMessageSend(#[source] mpsc::SendError),
#[error("failed to send message to CandidateBacking to get backed candidates")]
GetBackedCandidatesSend(#[source] mpsc::SendError),
#[error("failed to send return message with Inherents")] #[error("failed to send return message with Inherents")]
InherentDataReturnChannel, InherentDataReturnChannel,
#[error(
"backed candidate does not correspond to selected candidate; check logic in provisioner"
)]
BackedCandidateOrderingProblem,
} }
impl JobTrait for ProvisioningJob { impl JobTrait for ProvisioningJob {
...@@ -193,11 +168,10 @@ impl ProvisioningJob { ...@@ -193,11 +168,10 @@ impl ProvisioningJob {
sender: &mut impl SubsystemSender, sender: &mut impl SubsystemSender,
span: PerLeafSpan, span: PerLeafSpan,
) -> Result<(), Error> { ) -> Result<(), Error> {
use ProvisionerMessage::{ProvisionableData, RequestInherentData};
loop { loop {
futures::select! { futures::select! {
msg = self.receiver.next() => match msg { msg = self.receiver.next() => match msg {
Some(RequestInherentData(_, return_sender)) => { Some(ProvisionerMessage::RequestInherentData(_, return_sender)) => {
let _span = span.child("req-inherent-data"); let _span = span.child("req-inherent-data");
let _timer = self.metrics.time_request_inherent_data(); let _timer = self.metrics.time_request_inherent_data();
...@@ -207,7 +181,7 @@ impl ProvisioningJob { ...@@ -207,7 +181,7 @@ impl ProvisioningJob {
self.awaiting_inherent.push(return_sender); self.awaiting_inherent.push(return_sender);
} }
} }
Some(ProvisionableData(_, data)) => { Some(ProvisionerMessage::ProvisionableData(_, data)) => {
let span = span.child("provisionable-data"); let span = span.child("provisionable-data");
let _timer = self.metrics.time_provisionable_data(); let _timer = self.metrics.time_provisionable_data();
...@@ -235,8 +209,8 @@ impl ProvisioningJob { ...@@ -235,8 +209,8 @@ impl ProvisioningJob {
) { ) {
if let Err(err) = send_inherent_data( if let Err(err) = send_inherent_data(
self.relay_parent, self.relay_parent,
&self.signed_bitfields, self.signed_bitfields.clone(),
&self.backed_candidates, self.backed_candidates.clone(),
return_senders, return_senders,
sender, sender,
) )
...@@ -268,46 +242,25 @@ impl ProvisioningJob { ...@@ -268,46 +242,25 @@ impl ProvisioningJob {
} }
} }
type CoreAvailability = BitVec<bitvec::order::Lsb0, u8>; /// The provisioner is the subsystem best suited on the node side,
/// yet it lacks sufficient information to do weight based inherents limiting.
/// The provisioner is the subsystem best suited to choosing which specific /// This does the minimalistic checks and forwards a most likely
/// backed candidates and availability bitfields should be assembled into the /// too large set of bitfields, candidates, and dispute votes to
/// block. To engage this functionality, a /// the runtime. The `fn create_inherent` in the runtime is responsible
/// `ProvisionerMessage::RequestInherentData` is sent; the response is a set of /// to use a subset of these.
/// non-conflicting candidates and the appropriate bitfields. Non-conflicting
/// means that there are never two distinct parachain candidates included for
/// the same parachain and that new parachain candidates cannot be included
/// until the previous one either gets declared available or expired.
///
/// The main complication here is going to be around handling
/// occupied-core-assumptions. We might have candidates that are only
/// includable when some bitfields are included. And we might have candidates
/// that are not includable when certain bitfields are included.
///
/// When we're choosing bitfields to include, the rule should be simple:
/// maximize availability. So basically, include all bitfields. And then
/// choose a coherent set of candidates along with that.
async fn send_inherent_data( async fn send_inherent_data(
relay_parent: Hash, relay_parent: Hash,
bitfields: &[SignedAvailabilityBitfield], bitfields: SignedAvailabilityBitfields,
candidates: &[CandidateReceipt], candidate_receipts: Vec<CandidateReceipt>,
return_senders: Vec<oneshot::Sender<ProvisionerInherentData>>, return_senders: Vec<oneshot::Sender<ProvisionerInherentData>>,
from_job: &mut impl SubsystemSender, from_job: &mut impl SubsystemSender,
) -> Result<(), Error> { ) -> Result<(), Error> {
let availability_cores = request_availability_cores(relay_parent, from_job) let backed_candidates =
.await collect_backed_candidates(candidate_receipts, relay_parent, from_job).await?;
.await
.map_err(|err| Error::CanceledAvailabilityCores(err))??;
let bitfields = select_availability_bitfields(&availability_cores, bitfields); let disputes = collect_disputes(from_job).await?;
let candidates =
select_candidates(&availability_cores, &bitfields, candidates, relay_parent, from_job)
.await?;
let disputes = select_disputes(from_job).await?; let inherent_data = ProvisionerInherentData { bitfields, backed_candidates, disputes };
let inherent_data =
ProvisionerInherentData { bitfields, backed_candidates: candidates, disputes };
for return_sender in return_senders { for return_sender in return_senders {
return_sender return_sender
...@@ -318,120 +271,33 @@ async fn send_inherent_data( ...@@ -318,120 +271,33 @@ async fn send_inherent_data(
Ok(()) Ok(())
} }
/// In general, we want to pick all the bitfields. However, we have the following constraints: /// Collect backed candidates with a matching `relay_parent`.
/// async fn collect_backed_candidates(
/// - not more than one per validator candidate_receipts: Vec<CandidateReceipt>,
/// - each 1 bit must correspond to an occupied core
///
/// If we have too many, an arbitrary selection policy is fine. For purposes of maximizing availability,
/// we pick the one with the greatest number of 1 bits.
///
/// Note: This does not enforce any sorting precondition on the output; the ordering there will be unrelated
/// to the sorting of the input.
fn select_availability_bitfields(
cores: &[CoreState],
bitfields: &[SignedAvailabilityBitfield],
) -> Vec<SignedAvailabilityBitfield> {
let mut selected: BTreeMap<ValidatorIndex, SignedAvailabilityBitfield> = BTreeMap::new();
'a: for bitfield in bitfields.iter().cloned() {
if bitfield.payload().0.len() != cores.len() {
continue
}
let is_better = selected
.get(&bitfield.validator_index())
.map_or(true, |b| b.payload().0.count_ones() < bitfield.payload().0.count_ones());
if !is_better {
continue
}
for (idx, _) in cores.iter().enumerate().filter(|v| !v.1.is_occupied()) {
// Bit is set for an unoccupied core - invalid
if *bitfield.payload().0.get(idx).as_deref().unwrap_or(&false) {
continue 'a
}
}
let _ = selected.insert(bitfield.validator_index(), bitfield);
}
selected.into_iter().map(|(_, b)| b).collect()
}
/// Determine which cores are free, and then to the degree possible, pick a candidate appropriate to each free core.
async fn select_candidates(
availability_cores: &[CoreState],
bitfields: &[SignedAvailabilityBitfield],
candidates: &[CandidateReceipt],
relay_parent: Hash, relay_parent: Hash,
sender: &mut impl SubsystemSender, sender: &mut impl SubsystemSender,
) -> Result<Vec<BackedCandidate>, Error> { ) -> Result<Vec<BackedCandidate>, Error> {
let block_number = get_block_number_under_construction(relay_parent, sender).await?; let max_one_candidate_per_para = HashSet::<ParaId>::with_capacity(candidate_receipts.len());
let selected_candidates = candidate_receipts
let mut selected_candidates = .into_iter()
Vec::with_capacity(candidates.len().min(availability_cores.len())); .filter(|candidate_receipt| {
// assure the follow up query `GetBackedCandidate` succeeds
for (core_idx, core) in availability_cores.iter().enumerate() { candidate_receipt.descriptor().relay_parent == relay_parent
let (scheduled_core, assumption) = match core { })
CoreState::Scheduled(scheduled_core) => (scheduled_core, OccupiedCoreAssumption::Free), .scan(max_one_candidate_per_para, |unique, candidate_receipt| {
CoreState::Occupied(occupied_core) => { let para_id = candidate_receipt.descriptor().para_id;
if bitfields_indicate_availability(core_idx, bitfields, &occupied_core.availability) if unique.insert(para_id) {
{ Some(candidate_receipt.hash())
if let Some(ref scheduled_core) = occupied_core.next_up_on_available { } else {
(scheduled_core, OccupiedCoreAssumption::Included) tracing::debug!(
} else { target: LOG_TARGET,
continue ?para_id,
} "Duplicate candidate detected for para, only submitting one",
} else { );
if occupied_core.time_out_at != block_number { None
continue }
} })
if let Some(ref scheduled_core) = occupied_core.next_up_on_time_out { .collect::<Vec<CandidateHash>>();
(scheduled_core, OccupiedCoreAssumption::TimedOut)
} else {
continue
}
}
},
CoreState::Free => continue,
};
let validation_data = match request_persisted_validation_data(
relay_parent,
scheduled_core.para_id,
assumption,
sender,
)
.await
.await
.map_err(|err| Error::CanceledPersistedValidationData(err))??
{
Some(v) => v,