Skip to main content

polkadot_node_core_provisioner/
lib.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17//! The provisioner is responsible for assembling a relay chain block
18//! from a set of available parachain candidates of its choice.
19
20#![deny(missing_docs, unused_crate_dependencies)]
21
22use bitvec::vec::BitVec;
23use futures::{
24	channel::oneshot::{self, Canceled},
25	future::BoxFuture,
26	prelude::*,
27	stream::FuturesUnordered,
28	FutureExt,
29};
30use futures_timer::Delay;
31use polkadot_node_subsystem::{
32	messages::{
33		Ancestors, BackableCandidateRef, CandidateBackingMessage, ChainApiMessage,
34		ProspectiveParachainsMessage, ProvisionableData, ProvisionerInherentData,
35		ProvisionerMessage,
36	},
37	overseer, ActivatedLeaf, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, SpawnedSubsystem,
38	SubsystemError,
39};
40use polkadot_node_subsystem_util::{request_availability_cores, TimeoutExt};
41use polkadot_primitives::{
42	BackedCandidate, CandidateEvent, CoreIndex, CoreState, Hash, Id as ParaId,
43	SignedAvailabilityBitfield, ValidatorIndex,
44};
45use sc_consensus_slots::time_until_next_slot;
46use schnellru::{ByLength, LruMap};
47use std::{
48	collections::{BTreeMap, HashMap},
49	time::Duration,
50};
51mod disputes;
52mod error;
53mod metrics;
54
55pub use self::metrics::*;
56use error::{Error, FatalResult};
57
58#[cfg(test)]
59mod tests;
60
61/// How long to wait before proposing.
62const PRE_PROPOSE_TIMEOUT: std::time::Duration = core::time::Duration::from_millis(2000);
63/// Some timeout to ensure task won't hang around in the background forever on issues.
64const SEND_INHERENT_DATA_TIMEOUT: std::time::Duration = core::time::Duration::from_millis(500);
65
66const LOG_TARGET: &str = "parachain::provisioner";
67
68/// The provisioner subsystem.
69pub struct ProvisionerSubsystem {
70	metrics: Metrics,
71}
72
73impl ProvisionerSubsystem {
74	/// Create a new instance of the `ProvisionerSubsystem`.
75	pub fn new(metrics: Metrics) -> Self {
76		Self { metrics }
77	}
78}
79
80/// A per-relay-parent state for the provisioning subsystem.
81pub struct PerRelayParent {
82	leaf: ActivatedLeaf,
83	signed_bitfields: Vec<SignedAvailabilityBitfield>,
84	is_inherent_ready: bool,
85	awaiting_inherent: Vec<oneshot::Sender<ProvisionerInherentData>>,
86}
87
88impl PerRelayParent {
89	fn new(leaf: ActivatedLeaf) -> Self {
90		Self {
91			leaf,
92			signed_bitfields: Vec::new(),
93			is_inherent_ready: false,
94			awaiting_inherent: Vec::new(),
95		}
96	}
97}
98
99type InherentDelays = FuturesUnordered<BoxFuture<'static, Hash>>;
100type SlotDelays = FuturesUnordered<BoxFuture<'static, Hash>>;
101type InherentReceivers =
102	FuturesUnordered<BoxFuture<'static, (Hash, Result<ProvisionerInherentData, Canceled>)>>;
103
104#[overseer::subsystem(Provisioner, error=SubsystemError, prefix=self::overseer)]
105impl<Context> ProvisionerSubsystem {
106	fn start(self, ctx: Context) -> SpawnedSubsystem {
107		let future = async move {
108			run(ctx, self.metrics)
109				.await
110				.map_err(|e| SubsystemError::with_origin("provisioner", e))
111		}
112		.boxed();
113
114		SpawnedSubsystem { name: "provisioner-subsystem", future }
115	}
116}
117
118#[overseer::contextbounds(Provisioner, prefix = self::overseer)]
119async fn run<Context>(mut ctx: Context, metrics: Metrics) -> FatalResult<()> {
120	let mut inherent_delays = InherentDelays::new();
121	let mut inherent_receivers = InherentReceivers::new();
122	let mut slot_delays = SlotDelays::new();
123	let mut per_relay_parent = HashMap::new();
124	let mut inherents = LruMap::new(ByLength::new(16));
125
126	loop {
127		let result = run_iteration(
128			&mut ctx,
129			&mut per_relay_parent,
130			&mut inherent_delays,
131			&mut inherent_receivers,
132			&mut inherents,
133			&mut slot_delays,
134			&metrics,
135		)
136		.await;
137
138		match result {
139			Ok(()) => break,
140			err => crate::error::log_error(err)?,
141		}
142	}
143
144	Ok(())
145}
146
147#[overseer::contextbounds(Provisioner, prefix = self::overseer)]
148async fn run_iteration<Context>(
149	ctx: &mut Context,
150	per_relay_parent: &mut HashMap<Hash, PerRelayParent>,
151	inherent_delays: &mut InherentDelays,
152	inherent_receivers: &mut InherentReceivers,
153	inherents: &mut LruMap<Hash, ProvisionerInherentData>,
154	slot_delays: &mut SlotDelays,
155	metrics: &Metrics,
156) -> Result<(), Error> {
157	loop {
158		futures::select! {
159			from_overseer = ctx.recv().fuse() => {
160				// Map the error to ensure that the subsystem exits when the overseer is gone.
161				match from_overseer.map_err(Error::OverseerExited)? {
162					FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update)) =>
163						handle_active_leaves_update(ctx, update, per_relay_parent, inherent_delays, slot_delays, inherents, metrics).await?,
164					FromOrchestra::Signal(OverseerSignal::BlockFinalized(..)) => {},
165					FromOrchestra::Signal(OverseerSignal::Conclude) => return Ok(()),
166					FromOrchestra::Communication { msg } => {
167						handle_communication(ctx, per_relay_parent, msg, metrics).await?;
168					},
169				}
170			},
171			hash = slot_delays.select_next_some() => {
172				gum::debug!(target: LOG_TARGET, leaf_hash=?hash, "Slot start, preparing debug inherent");
173
174				let Some(state) = per_relay_parent.get_mut(&hash) else {
175					continue
176				};
177
178				// Create the inherent data just to record the backed candidates.
179				let (inherent_tx, inherent_rx) = oneshot::channel();
180				let task = async move {
181					match inherent_rx.await {
182						Ok(res) => (hash, Ok(res)),
183						Err(e) => (hash, Err(e)),
184					}
185				}
186				.boxed();
187
188				inherent_receivers.push(task);
189
190				send_inherent_data_bg(ctx, &state, vec![inherent_tx], metrics.clone()).await?;
191			},
192			(hash, inherent_data) = inherent_receivers.select_next_some() => {
193				let Ok(inherent_data) = inherent_data else {
194					continue
195				};
196
197				gum::trace!(
198					target: LOG_TARGET,
199					relay_parent = ?hash,
200					"Debug Inherent Data became ready"
201				);
202				inherents.insert(hash, inherent_data);
203			}
204			hash = inherent_delays.select_next_some() => {
205				if let Some(state) = per_relay_parent.get_mut(&hash) {
206					state.is_inherent_ready = true;
207
208					gum::trace!(
209						target: LOG_TARGET,
210						relay_parent = ?hash,
211						"Inherent Data became ready"
212					);
213
214					let return_senders = std::mem::take(&mut state.awaiting_inherent);
215					if !return_senders.is_empty() {
216						send_inherent_data_bg(ctx, &state, return_senders, metrics.clone()).await?;
217					}
218				}
219			}
220		}
221	}
222}
223
224#[overseer::contextbounds(Provisioner, prefix = self::overseer)]
225async fn handle_active_leaves_update<Context>(
226	ctx: &mut Context,
227	update: ActiveLeavesUpdate,
228	per_relay_parent: &mut HashMap<Hash, PerRelayParent>,
229	inherent_delays: &mut InherentDelays,
230	slot_delays: &mut SlotDelays,
231	inherents: &mut LruMap<Hash, ProvisionerInherentData>,
232	metrics: &Metrics,
233) -> Result<(), Error> {
234	gum::trace!(target: LOG_TARGET, "Handle ActiveLeavesUpdate");
235	for deactivated in &update.deactivated {
236		per_relay_parent.remove(deactivated);
237	}
238
239	let Some(leaf) = update.activated else { return Ok(()) };
240
241	gum::trace!(target: LOG_TARGET, leaf_hash=?leaf.hash, "Adding delay");
242	let delay_fut = Delay::new(PRE_PROPOSE_TIMEOUT).map(move |_| leaf.hash).boxed();
243	per_relay_parent.insert(leaf.hash, PerRelayParent::new(leaf.clone()));
244	inherent_delays.push(delay_fut);
245
246	let slot_delay = time_until_next_slot(Duration::from_millis(6000));
247	gum::debug!(target: LOG_TARGET, leaf_hash=?leaf.hash, "Expecting next slot in {}ms", slot_delay.as_millis());
248
249	let slot_delay_task =
250		Delay::new(slot_delay + PRE_PROPOSE_TIMEOUT).map(move |_| leaf.hash).boxed();
251	slot_delays.push(slot_delay_task);
252
253	let Ok(Ok(candidate_events)) =
254		polkadot_node_subsystem_util::request_candidate_events(leaf.hash, ctx.sender())
255			.await
256			.await
257	else {
258		gum::warn!(target: LOG_TARGET, leaf_hash=?leaf.hash, "Failed to fetch candidate events");
259
260		return Ok(());
261	};
262
263	let in_block_count = candidate_events
264		.into_iter()
265		.filter(|event| matches!(event, CandidateEvent::CandidateBacked(_, _, _, _)))
266		.count() as isize;
267
268	let (tx, rx) = oneshot::channel();
269	ctx.send_message(ChainApiMessage::BlockHeader(leaf.hash, tx)).await;
270
271	let Ok(Some(header)) = rx.await.unwrap_or_else(|err| {
272		gum::warn!(target: LOG_TARGET, hash = ?leaf.hash, ?err, "Missing header for block");
273		Ok(None)
274	}) else {
275		return Ok(());
276	};
277
278	gum::trace!(target: LOG_TARGET, hash = ?header.parent_hash, "Looking up debug inherent");
279
280	// Now, let's get the candidate count from our own inherent built earlier.
281	// The inherent is stored under the parent hash.
282	let Some(inherent) = inherents.get(&header.parent_hash) else { return Ok(()) };
283
284	let diff = inherent.backed_candidates.len() as isize - in_block_count;
285	gum::debug!(target: LOG_TARGET,
286		 ?diff,
287		 ?in_block_count,
288		 local_count = ?inherent.backed_candidates.len(),
289		 leaf_hash=?leaf.hash, "Offchain vs on-chain backing update");
290
291	metrics.observe_backable_vs_in_block(diff);
292	Ok(())
293}
294
295#[overseer::contextbounds(Provisioner, prefix = self::overseer)]
296async fn handle_communication<Context>(
297	ctx: &mut Context,
298	per_relay_parent: &mut HashMap<Hash, PerRelayParent>,
299	message: ProvisionerMessage,
300	metrics: &Metrics,
301) -> Result<(), Error> {
302	match message {
303		ProvisionerMessage::RequestInherentData(relay_parent, return_sender) => {
304			gum::trace!(target: LOG_TARGET, ?relay_parent, "Inherent data got requested.");
305
306			if let Some(state) = per_relay_parent.get_mut(&relay_parent) {
307				if state.is_inherent_ready {
308					gum::trace!(target: LOG_TARGET, ?relay_parent, "Calling send_inherent_data.");
309					send_inherent_data_bg(ctx, &state, vec![return_sender], metrics.clone())
310						.await?;
311				} else {
312					gum::trace!(
313						target: LOG_TARGET,
314						?relay_parent,
315						"Queuing inherent data request (inherent data not yet ready)."
316					);
317					state.awaiting_inherent.push(return_sender);
318				}
319			}
320		},
321		ProvisionerMessage::ProvisionableData(relay_parent, data) => {
322			if let Some(state) = per_relay_parent.get_mut(&relay_parent) {
323				let _timer = metrics.time_provisionable_data();
324
325				gum::trace!(target: LOG_TARGET, ?relay_parent, "Received provisionable data: {:?}", &data);
326
327				note_provisionable_data(state, data);
328			}
329		},
330	}
331
332	Ok(())
333}
334
335#[overseer::contextbounds(Provisioner, prefix = self::overseer)]
336async fn send_inherent_data_bg<Context>(
337	ctx: &mut Context,
338	per_relay_parent: &PerRelayParent,
339	return_senders: Vec<oneshot::Sender<ProvisionerInherentData>>,
340	metrics: Metrics,
341) -> Result<(), Error> {
342	let leaf = per_relay_parent.leaf.clone();
343	let signed_bitfields = per_relay_parent.signed_bitfields.clone();
344	let mut sender = ctx.sender().clone();
345
346	let bg = async move {
347		let _timer = metrics.time_request_inherent_data();
348
349		gum::trace!(
350			target: LOG_TARGET,
351			relay_parent = ?leaf.hash,
352			"Sending inherent data in background."
353		);
354
355		let send_result =
356			send_inherent_data(&leaf, &signed_bitfields, return_senders, &mut sender, &metrics) // Make sure call is not taking forever:
357				.timeout(SEND_INHERENT_DATA_TIMEOUT)
358				.map(|v| match v {
359					Some(r) => r,
360					None => Err(Error::SendInherentDataTimeout),
361				});
362
363		match send_result.await {
364			Err(err) => {
365				if let Error::CanceledBackedCandidates(_) = err {
366					gum::debug!(
367						target: LOG_TARGET,
368						err = ?err,
369						"Failed to assemble or send inherent data - block got likely obsoleted already."
370					);
371				} else {
372					gum::warn!(target: LOG_TARGET, err = ?err, "failed to assemble or send inherent data");
373				}
374				metrics.on_inherent_data_request(Err(()));
375			},
376			Ok(()) => {
377				metrics.on_inherent_data_request(Ok(()));
378				gum::debug!(
379					target: LOG_TARGET,
380					signed_bitfield_count = signed_bitfields.len(),
381					leaf_hash = ?leaf.hash,
382					"inherent data sent successfully"
383				);
384				metrics.observe_inherent_data_bitfields_count(signed_bitfields.len());
385			},
386		}
387	};
388
389	ctx.spawn("send-inherent-data", bg.boxed())
390		.map_err(|_| Error::FailedToSpawnBackgroundTask)?;
391
392	Ok(())
393}
394
395fn note_provisionable_data(
396	per_relay_parent: &mut PerRelayParent,
397	provisionable_data: ProvisionableData,
398) {
399	match provisionable_data {
400		ProvisionableData::Bitfield(_, signed_bitfield) => {
401			per_relay_parent.signed_bitfields.push(signed_bitfield)
402		},
403		// We choose not to punish these forms of misbehavior for the time being.
404		// Risks from misbehavior are sufficiently mitigated at the protocol level
405		// via reputation changes. Punitive actions here may become desirable
406		// enough to dedicate time to in the future.
407		ProvisionableData::MisbehaviorReport(_, _, _) => {},
408		// We wait and do nothing here, preferring to initiate a dispute after the
409		// parablock candidate is included for the following reasons:
410		//
411		// 1. A dispute for a candidate triggered at any point before the candidate
412		// has been made available, including the backing stage, can't be
413		// guaranteed to conclude. Non-concluding disputes are unacceptable.
414		// 2. Candidates which haven't been made available don't pose a security
415		// risk as they can not be included, approved, or finalized.
416		//
417		// Currently we rely on approval checkers to trigger disputes for bad
418		// parablocks once they are included. But we can do slightly better by
419		// allowing disagreeing backers to record their disagreement and initiate a
420		// dispute once the parablock in question has been included. This potential
421		// change is tracked by: https://github.com/paritytech/polkadot/issues/3232
422		ProvisionableData::Dispute(_, _) => {},
423	}
424}
425
426type CoreAvailability = BitVec<u8, bitvec::order::Lsb0>;
427
428/// The provisioner is the subsystem best suited to choosing which specific
429/// backed candidates and availability bitfields should be assembled into the
430/// block. To engage this functionality, a
431/// `ProvisionerMessage::RequestInherentData` is sent; the response is a set of
432/// non-conflicting candidates and the appropriate bitfields. Non-conflicting
433/// means that there are never two distinct parachain candidates included for
434/// the same parachain and that new parachain candidates cannot be included
435/// until the previous one either gets declared available or expired.
436///
437/// The main complication here is going to be around handling
438/// occupied-core-assumptions. We might have candidates that are only
439/// includable when some bitfields are included. And we might have candidates
440/// that are not includable when certain bitfields are included.
441///
442/// When we're choosing bitfields to include, the rule should be simple:
443/// maximize availability. So basically, include all bitfields. And then
444/// choose a coherent set of candidates along with that.
445async fn send_inherent_data(
446	leaf: &ActivatedLeaf,
447	bitfields: &[SignedAvailabilityBitfield],
448	return_senders: Vec<oneshot::Sender<ProvisionerInherentData>>,
449	from_job: &mut impl overseer::ProvisionerSenderTrait,
450	metrics: &Metrics,
451) -> Result<(), Error> {
452	gum::trace!(
453		target: LOG_TARGET,
454		relay_parent = ?leaf.hash,
455		"Requesting availability cores"
456	);
457	let availability_cores = request_availability_cores(leaf.hash, from_job)
458		.await
459		.await
460		.map_err(|err| Error::CanceledAvailabilityCores(err))??;
461
462	gum::trace!(
463		target: LOG_TARGET,
464		relay_parent = ?leaf.hash,
465		"Selecting disputes"
466	);
467
468	let disputes = disputes::prioritized_selection::select_disputes(from_job, metrics, leaf).await;
469
470	gum::trace!(
471		target: LOG_TARGET,
472		relay_parent = ?leaf.hash,
473		"Selected disputes"
474	);
475
476	let bitfields = select_availability_bitfields(&availability_cores, bitfields, &leaf.hash);
477
478	gum::trace!(
479		target: LOG_TARGET,
480		relay_parent = ?leaf.hash,
481		"Selected bitfields"
482	);
483
484	let candidates = select_candidates(&availability_cores, &bitfields, leaf, from_job).await?;
485
486	gum::trace!(
487		target: LOG_TARGET,
488		relay_parent = ?leaf.hash,
489		"Selected candidates"
490	);
491
492	gum::debug!(
493		target: LOG_TARGET,
494		availability_cores_len = availability_cores.len(),
495		disputes_count = disputes.len(),
496		bitfields_count = bitfields.len(),
497		candidates_count = candidates.len(),
498		leaf_hash = ?leaf.hash,
499		"inherent data prepared",
500	);
501
502	let inherent_data =
503		ProvisionerInherentData { bitfields, backed_candidates: candidates, disputes };
504
505	gum::trace!(
506		target: LOG_TARGET,
507		relay_parent = ?leaf.hash,
508		"Sending back inherent data to requesters."
509	);
510
511	for return_sender in return_senders {
512		return_sender
513			.send(inherent_data.clone())
514			.map_err(|_data| Error::InherentDataReturnChannel)?;
515	}
516
517	Ok(())
518}
519
520/// In general, we want to pick all the bitfields. However, we have the following constraints:
521///
522/// - not more than one per validator
523/// - each 1 bit must correspond to an occupied core
524///
525/// If we have too many, an arbitrary selection policy is fine. For purposes of maximizing
526/// availability, we pick the one with the greatest number of 1 bits.
527///
528/// Note: This does not enforce any sorting precondition on the output; the ordering there will be
529/// unrelated to the sorting of the input.
530fn select_availability_bitfields(
531	cores: &[CoreState],
532	bitfields: &[SignedAvailabilityBitfield],
533	leaf_hash: &Hash,
534) -> Vec<SignedAvailabilityBitfield> {
535	let mut selected: BTreeMap<ValidatorIndex, SignedAvailabilityBitfield> = BTreeMap::new();
536
537	gum::debug!(
538		target: LOG_TARGET,
539		bitfields_count = bitfields.len(),
540		?leaf_hash,
541		"bitfields count before selection"
542	);
543
544	'a: for bitfield in bitfields.iter().cloned() {
545		if bitfield.payload().0.len() != cores.len() {
546			gum::debug!(target: LOG_TARGET, ?leaf_hash, "dropping bitfield due to length mismatch");
547			continue;
548		}
549
550		let is_better = selected
551			.get(&bitfield.validator_index())
552			.map_or(true, |b| b.payload().0.count_ones() < bitfield.payload().0.count_ones());
553
554		if !is_better {
555			gum::trace!(
556				target: LOG_TARGET,
557				val_idx = bitfield.validator_index().0,
558				?leaf_hash,
559				"dropping bitfield due to duplication - the better one is kept"
560			);
561			continue;
562		}
563
564		for (idx, _) in cores.iter().enumerate().filter(|v| !v.1.is_occupied()) {
565			// Bit is set for an unoccupied core - invalid
566			if *bitfield.payload().0.get(idx).as_deref().unwrap_or(&false) {
567				gum::debug!(
568					target: LOG_TARGET,
569					val_idx = bitfield.validator_index().0,
570					?leaf_hash,
571					"dropping invalid bitfield - bit is set for an unoccupied core"
572				);
573				continue 'a;
574			}
575		}
576
577		let _ = selected.insert(bitfield.validator_index(), bitfield);
578	}
579
580	gum::debug!(
581		target: LOG_TARGET,
582		?leaf_hash,
583		"selected {} of all {} bitfields (each bitfield is from a unique validator)",
584		selected.len(),
585		bitfields.len()
586	);
587
588	selected.into_values().collect()
589}
590
591/// Requests backable candidates from Prospective Parachains subsystem
592/// based on core states.
593async fn request_backable_candidates(
594	availability_cores: &[CoreState],
595	bitfields: &[SignedAvailabilityBitfield],
596	leaf: &ActivatedLeaf,
597	sender: &mut impl overseer::ProvisionerSenderTrait,
598) -> Result<HashMap<ParaId, Vec<BackableCandidateRef>>, Error> {
599	let block_number_under_construction = leaf.number + 1;
600
601	// Record how many cores are scheduled for each paraid. Use a BTreeMap because
602	// we'll need to iterate through them.
603	let mut scheduled_cores_per_para: BTreeMap<ParaId, usize> = BTreeMap::new();
604	// The on-chain ancestors of a para present in availability-cores.
605	let mut ancestors: HashMap<ParaId, Ancestors> =
606		HashMap::with_capacity(availability_cores.len());
607
608	for (core_idx, core) in availability_cores.iter().enumerate() {
609		let core_idx = CoreIndex(core_idx as u32);
610		match core {
611			CoreState::Scheduled(scheduled_core) => {
612				*scheduled_cores_per_para.entry(scheduled_core.para_id).or_insert(0) += 1;
613			},
614			CoreState::Occupied(occupied_core) => {
615				let is_available = bitfields_indicate_availability(
616					core_idx.0 as usize,
617					bitfields,
618					&occupied_core.availability,
619				);
620
621				if is_available {
622					ancestors
623						.entry(occupied_core.para_id())
624						.or_default()
625						.insert(occupied_core.candidate_hash);
626
627					if let Some(ref scheduled_core) = occupied_core.next_up_on_available {
628						// Request a new backable candidate for the newly scheduled para id.
629						*scheduled_cores_per_para.entry(scheduled_core.para_id).or_insert(0) += 1;
630					}
631				} else if occupied_core.time_out_at <= block_number_under_construction {
632					// Timed out before being available.
633
634					if let Some(ref scheduled_core) = occupied_core.next_up_on_time_out {
635						// Candidate's availability timed out, practically same as scheduled.
636						*scheduled_cores_per_para.entry(scheduled_core.para_id).or_insert(0) += 1;
637					}
638				} else {
639					// Not timed out and not available.
640					ancestors
641						.entry(occupied_core.para_id())
642						.or_default()
643						.insert(occupied_core.candidate_hash);
644				}
645			},
646			CoreState::Free => continue,
647		};
648	}
649
650	let mut selected_candidates: HashMap<ParaId, Vec<BackableCandidateRef>> =
651		HashMap::with_capacity(scheduled_cores_per_para.len());
652
653	for (para_id, core_count) in scheduled_cores_per_para {
654		let para_ancestors = ancestors.remove(&para_id).unwrap_or_default();
655
656		let response =
657			get_backable_candidates(leaf.hash, para_id, para_ancestors, core_count as u32, sender)
658				.await?;
659
660		if response.is_empty() {
661			gum::debug!(
662				target: LOG_TARGET,
663				leaf_hash = ?leaf.hash,
664				?para_id,
665				"No backable candidate returned by prospective parachains",
666			);
667			continue;
668		}
669
670		selected_candidates.insert(para_id, response);
671	}
672
673	Ok(selected_candidates)
674}
675
676/// Determine which cores are free, and then to the degree possible, pick a candidate appropriate to
677/// each free core.
678async fn select_candidates(
679	availability_cores: &[CoreState],
680	bitfields: &[SignedAvailabilityBitfield],
681	leaf: &ActivatedLeaf,
682	sender: &mut impl overseer::ProvisionerSenderTrait,
683) -> Result<Vec<BackedCandidate>, Error> {
684	gum::trace!(
685		target: LOG_TARGET,
686		leaf_hash=?leaf.hash,
687		"before GetBackedCandidates"
688	);
689
690	let selected_candidates =
691		request_backable_candidates(availability_cores, bitfields, leaf, sender).await?;
692	gum::debug!(target: LOG_TARGET, ?selected_candidates, "Got backable candidates");
693
694	// now get the backed candidates corresponding to these candidate receipts
695	let (tx, rx) = oneshot::channel();
696	sender.send_unbounded_message(CandidateBackingMessage::GetBackableCandidates {
697		candidates: selected_candidates.clone(),
698		sender: tx,
699	});
700	let candidates = rx.await.map_err(|err| Error::CanceledBackedCandidates(err))?;
701	gum::trace!(
702		target: LOG_TARGET,
703		leaf_hash=?leaf.hash,
704		"Got {} backed candidates", candidates.len()
705	);
706
707	// keep only one candidate with validation code.
708	let mut with_validation_code = false;
709	// merge the candidates into a common collection, preserving the order
710	let mut merged_candidates = Vec::with_capacity(availability_cores.len());
711
712	for para_candidates in candidates.into_values() {
713		for candidate in para_candidates {
714			if candidate.candidate().commitments.new_validation_code.is_some() {
715				if with_validation_code {
716					break;
717				} else {
718					with_validation_code = true;
719				}
720			}
721
722			merged_candidates.push(candidate);
723		}
724	}
725
726	gum::debug!(
727		target: LOG_TARGET,
728		n_candidates = merged_candidates.len(),
729		n_cores = availability_cores.len(),
730		leaf=?leaf.hash,
731		"Selected backed candidates",
732	);
733
734	Ok(merged_candidates)
735}
736
737/// Requests backable candidates from Prospective Parachains based on
738/// the given ancestors in the fragment chain. The ancestors may not be ordered.
739async fn get_backable_candidates(
740	leaf: Hash,
741	para_id: ParaId,
742	ancestors: Ancestors,
743	count: u32,
744	sender: &mut impl overseer::ProvisionerSenderTrait,
745) -> Result<Vec<BackableCandidateRef>, Error> {
746	let (tx, rx) = oneshot::channel();
747	sender
748		.send_message(ProspectiveParachainsMessage::GetBackableCandidates {
749			leaf,
750			para_id,
751			count,
752			ancestors,
753			sender: tx,
754		})
755		.await;
756
757	rx.await.map_err(Error::CanceledBackableCandidates)
758}
759
760/// The availability bitfield for a given core is the transpose
761/// of a set of signed availability bitfields. It goes like this:
762///
763/// - construct a transverse slice along `core_idx`
764/// - bitwise-or it with the availability slice
765/// - count the 1 bits, compare to the total length; true on 2/3+
766fn bitfields_indicate_availability(
767	core_idx: usize,
768	bitfields: &[SignedAvailabilityBitfield],
769	availability: &CoreAvailability,
770) -> bool {
771	let mut availability = availability.clone();
772	let availability_len = availability.len();
773
774	for bitfield in bitfields {
775		let validator_idx = bitfield.validator_index().0 as usize;
776		match availability.get_mut(validator_idx) {
777			None => {
778				// in principle, this function might return a `Result<bool, Error>` so that we can
779				// more clearly express this error condition however, in practice, that would just
780				// push off an error-handling routine which would look a whole lot like this one.
781				// simpler to just handle the error internally here.
782				gum::warn!(
783					target: LOG_TARGET,
784					validator_idx = %validator_idx,
785					availability_len = %availability_len,
786					"attempted to set a transverse bit at idx {} which is greater than bitfield size {}",
787					validator_idx,
788					availability_len,
789				);
790
791				return false;
792			},
793			Some(mut bit_mut) => *bit_mut |= bitfield.payload().0[core_idx],
794		}
795	}
796
797	3 * availability.count_ones() >= 2 * availability.len()
798}