referrerpolicy=no-referrer-when-downgrade

polkadot_node_core_approval_voting/
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 Approval Voting Subsystem.
18//!
19//! This subsystem is responsible for determining candidates to do approval checks
20//! on, performing those approval checks, and tracking the assignments and approvals
21//! of others. It uses this information to determine when candidates and blocks have
22//! been sufficiently approved to finalize.
23
24use futures_timer::Delay;
25use polkadot_node_primitives::{
26	approval::{
27		v1::{BlockApprovalMeta, DelayTranche},
28		v2::{
29			AssignmentCertKindV2, BitfieldError, CandidateBitfield, CoreBitfield,
30			IndirectAssignmentCertV2, IndirectSignedApprovalVoteV2,
31		},
32	},
33	ValidationResult, DISPUTE_WINDOW,
34};
35use polkadot_node_subsystem::{
36	errors::RecoveryError,
37	messages::{
38		ApprovalCheckError, ApprovalCheckResult, ApprovalDistributionMessage,
39		ApprovalVotingMessage, AssignmentCheckError, AssignmentCheckResult,
40		AvailabilityRecoveryMessage, BlockDescription, CandidateValidationMessage, ChainApiMessage,
41		ChainSelectionMessage, CheckedIndirectAssignment, CheckedIndirectSignedApprovalVote,
42		DisputeCoordinatorMessage, HighestApprovedAncestorBlock, PvfExecKind, RuntimeApiMessage,
43		RuntimeApiRequest,
44	},
45	overseer, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError, SubsystemResult,
46	SubsystemSender,
47};
48use polkadot_node_subsystem_util::{
49	self,
50	database::Database,
51	metrics::{self, prometheus},
52	runtime::{Config as RuntimeInfoConfig, ExtendedSessionInfo, RuntimeInfo},
53	TimeoutExt,
54};
55use polkadot_primitives::{
56	ApprovalVoteMultipleCandidates, BlockNumber, CandidateHash, CandidateIndex,
57	CandidateReceiptV2 as CandidateReceipt, CoalescedApprovalCandidateHashes, CoreIndex,
58	GroupIndex, Hash, SessionIndex, SessionInfo, ValidatorId, ValidatorIndex, ValidatorPair,
59	ValidatorSignature,
60};
61use sc_keystore::LocalKeystore;
62use sp_application_crypto::Pair;
63use sp_consensus::SyncOracle;
64use sp_consensus_slots::Slot;
65use std::time::Instant;
66
67// The max number of blocks we keep track of assignments gathering times. Normally,
68// this would never be reached because we prune the data on finalization, but we need
69// to also ensure the data is not growing unecessarily large.
70const MAX_BLOCKS_WITH_ASSIGNMENT_TIMESTAMPS: u32 = 100;
71
72use futures::{
73	channel::oneshot,
74	future::{BoxFuture, RemoteHandle},
75	prelude::*,
76	stream::FuturesUnordered,
77	StreamExt,
78};
79
80use std::{
81	cmp::min,
82	collections::{
83		btree_map::Entry as BTMEntry, hash_map::Entry as HMEntry, BTreeMap, HashMap, HashSet,
84	},
85	sync::Arc,
86	time::Duration,
87};
88
89use schnellru::{ByLength, LruMap};
90
91use approval_checking::RequiredTranches;
92use bitvec::{order::Lsb0, vec::BitVec};
93pub use criteria::{AssignmentCriteria, Config as AssignmentConfig, RealAssignmentCriteria};
94use persisted_entries::{ApprovalEntry, BlockEntry, CandidateEntry};
95use polkadot_node_primitives::approval::time::{
96	slot_number_to_tick, Clock, ClockExt, DelayedApprovalTimer, SystemClock, Tick,
97};
98
99mod approval_checking;
100pub mod approval_db;
101mod backend;
102pub mod criteria;
103mod import;
104mod ops;
105mod persisted_entries;
106
107use crate::{
108	approval_checking::{Check, TranchesToApproveResult},
109	approval_db::common::{Config as DatabaseConfig, DbBackend},
110	backend::{Backend, OverlayedBackend},
111	criteria::InvalidAssignmentReason,
112	persisted_entries::OurApproval,
113};
114
115#[cfg(test)]
116mod tests;
117
118const APPROVAL_CHECKING_TIMEOUT: Duration = Duration::from_secs(120);
119/// How long are we willing to wait for approval signatures?
120///
121/// Value rather arbitrarily: Should not be hit in practice, it exists to more easily diagnose dead
122/// lock issues for example.
123const WAIT_FOR_SIGS_TIMEOUT: Duration = Duration::from_millis(500);
124const APPROVAL_CACHE_SIZE: u32 = 1024;
125
126/// The maximum number of times we retry to approve a block if is still needed.
127const MAX_APPROVAL_RETRIES: u32 = 16;
128
129const APPROVAL_DELAY: Tick = 2;
130pub(crate) const LOG_TARGET: &str = "parachain::approval-voting";
131
132// The max number of ticks we delay sending the approval after we are ready to issue the approval
133const MAX_APPROVAL_COALESCE_WAIT_TICKS: Tick = 12;
134
135// If the node restarted and the tranche has passed without the assignment
136// being trigger, we won't trigger the assignment at restart because we don't have
137// an wakeup schedule for it.
138// The solution, is to always schedule a wake up after the restart and let the
139// process_wakeup to decide if the assignment needs to be triggered.
140// We need to have a delay after restart to give time to the node to catch up with
141// messages and not trigger its assignment unnecessarily, because it hasn't seen
142// the assignments from the other validators.
143const RESTART_WAKEUP_DELAY: Tick = 12;
144
145/// Configuration for the approval voting subsystem
146#[derive(Debug, Clone)]
147pub struct Config {
148	/// The column family in the DB where approval-voting data is stored.
149	pub col_approval_data: u32,
150	/// The slot duration of the consensus algorithm, in milliseconds. Should be evenly
151	/// divisible by 500.
152	pub slot_duration_millis: u64,
153}
154
155// The mode of the approval voting subsystem. It should start in a `Syncing` mode when it first
156// starts, and then once it's reached the head of the chain it should move into the `Active` mode.
157//
158// In `Active` mode, the node is an active participant in the approvals protocol. When syncing,
159// the node follows the new incoming blocks and finalized number, but does not yet participate.
160//
161// When transitioning from `Syncing` to `Active`, the node notifies the `ApprovalDistribution`
162// subsystem of all unfinalized blocks and the candidates included within them, as well as all
163// votes that the local node itself has cast on candidates within those blocks.
164enum Mode {
165	Active,
166	Syncing(Box<dyn SyncOracle + Send>),
167}
168
169/// The approval voting subsystem.
170pub struct ApprovalVotingSubsystem {
171	/// `LocalKeystore` is needed for assignment keys, but not necessarily approval keys.
172	///
173	/// We do a lot of VRF signing and need the keys to have low latency.
174	keystore: Arc<LocalKeystore>,
175	db_config: DatabaseConfig,
176	slot_duration_millis: u64,
177	db: Arc<dyn Database>,
178	mode: Mode,
179	metrics: Metrics,
180	clock: Arc<dyn Clock + Send + Sync>,
181	spawner: Arc<dyn overseer::gen::Spawner + 'static>,
182	/// The maximum time we retry to approve a block if it is still needed and PoV fetch failed.
183	max_approval_retries: u32,
184	/// The backoff before we retry the approval.
185	retry_backoff: Duration,
186}
187
188#[derive(Clone)]
189struct MetricsInner {
190	imported_candidates_total: prometheus::Counter<prometheus::U64>,
191	assignments_produced: prometheus::Histogram,
192	approvals_produced_total: prometheus::CounterVec<prometheus::U64>,
193	no_shows_total: prometheus::Counter<prometheus::U64>,
194	// The difference from `no_shows_total` is that this counts all observed no-shows at any
195	// moment in time. While `no_shows_total` catches that the no-shows at the moment the candidate
196	// is approved, approvals might arrive late and `no_shows_total` wouldn't catch that number.
197	observed_no_shows: prometheus::Counter<prometheus::U64>,
198	approved_by_one_third: prometheus::Counter<prometheus::U64>,
199	wakeups_triggered_total: prometheus::Counter<prometheus::U64>,
200	coalesced_approvals_buckets: prometheus::Histogram,
201	coalesced_approvals_delay: prometheus::Histogram,
202	candidate_approval_time_ticks: prometheus::Histogram,
203	block_approval_time_ticks: prometheus::Histogram,
204	time_db_transaction: prometheus::Histogram,
205	time_recover_and_approve: prometheus::Histogram,
206	candidate_signatures_requests_total: prometheus::Counter<prometheus::U64>,
207	unapproved_candidates_in_unfinalized_chain: prometheus::Gauge<prometheus::U64>,
208	// The time it takes in each stage to gather enough assignments.
209	// We defined a `stage` as being the entire process of gathering enough assignments to
210	// be able to approve a candidate:
211	// E.g:
212	// - Stage 0: We wait for the needed_approvals assignments to be gathered.
213	// - Stage 1: We wait for enough tranches to cover all no-shows in stage 0.
214	// - Stage 2: We wait for enough tranches to cover all no-shows  of stage 1.
215	assignments_gathering_time_by_stage: prometheus::HistogramVec,
216}
217
218/// Approval Voting metrics.
219#[derive(Default, Clone)]
220pub struct Metrics(Option<MetricsInner>);
221
222impl Metrics {
223	fn on_candidate_imported(&self) {
224		if let Some(metrics) = &self.0 {
225			metrics.imported_candidates_total.inc();
226		}
227	}
228
229	fn on_assignment_produced(&self, tranche: DelayTranche) {
230		if let Some(metrics) = &self.0 {
231			metrics.assignments_produced.observe(tranche as f64);
232		}
233	}
234
235	fn on_approval_coalesce(&self, num_coalesced: u32) {
236		if let Some(metrics) = &self.0 {
237			// Count how many candidates we covered with this coalesced approvals,
238			// so that the heat-map really gives a good understanding of the scales.
239			for _ in 0..num_coalesced {
240				metrics.coalesced_approvals_buckets.observe(num_coalesced as f64)
241			}
242		}
243	}
244
245	fn on_delayed_approval(&self, delayed_ticks: u64) {
246		if let Some(metrics) = &self.0 {
247			metrics.coalesced_approvals_delay.observe(delayed_ticks as f64)
248		}
249	}
250
251	fn on_approval_stale(&self) {
252		if let Some(metrics) = &self.0 {
253			metrics.approvals_produced_total.with_label_values(&["stale"]).inc()
254		}
255	}
256
257	fn on_approval_invalid(&self) {
258		if let Some(metrics) = &self.0 {
259			metrics.approvals_produced_total.with_label_values(&["invalid"]).inc()
260		}
261	}
262
263	fn on_approval_unavailable(&self) {
264		if let Some(metrics) = &self.0 {
265			metrics.approvals_produced_total.with_label_values(&["unavailable"]).inc()
266		}
267	}
268
269	fn on_approval_error(&self) {
270		if let Some(metrics) = &self.0 {
271			metrics.approvals_produced_total.with_label_values(&["internal error"]).inc()
272		}
273	}
274
275	fn on_approval_produced(&self) {
276		if let Some(metrics) = &self.0 {
277			metrics.approvals_produced_total.with_label_values(&["success"]).inc()
278		}
279	}
280
281	fn on_no_shows(&self, n: usize) {
282		if let Some(metrics) = &self.0 {
283			metrics.no_shows_total.inc_by(n as u64);
284		}
285	}
286
287	fn on_observed_no_shows(&self, n: usize) {
288		if let Some(metrics) = &self.0 {
289			metrics.observed_no_shows.inc_by(n as u64);
290		}
291	}
292
293	fn on_approved_by_one_third(&self) {
294		if let Some(metrics) = &self.0 {
295			metrics.approved_by_one_third.inc();
296		}
297	}
298
299	fn on_wakeup(&self) {
300		if let Some(metrics) = &self.0 {
301			metrics.wakeups_triggered_total.inc();
302		}
303	}
304
305	fn on_candidate_approved(&self, ticks: Tick) {
306		if let Some(metrics) = &self.0 {
307			metrics.candidate_approval_time_ticks.observe(ticks as f64);
308		}
309	}
310
311	fn on_block_approved(&self, ticks: Tick) {
312		if let Some(metrics) = &self.0 {
313			metrics.block_approval_time_ticks.observe(ticks as f64);
314		}
315	}
316
317	fn on_candidate_signatures_request(&self) {
318		if let Some(metrics) = &self.0 {
319			metrics.candidate_signatures_requests_total.inc();
320		}
321	}
322
323	fn time_db_transaction(&self) -> Option<metrics::prometheus::prometheus::HistogramTimer> {
324		self.0.as_ref().map(|metrics| metrics.time_db_transaction.start_timer())
325	}
326
327	fn time_recover_and_approve(&self) -> Option<metrics::prometheus::prometheus::HistogramTimer> {
328		self.0.as_ref().map(|metrics| metrics.time_recover_and_approve.start_timer())
329	}
330
331	fn on_unapproved_candidates_in_unfinalized_chain(&self, count: usize) {
332		if let Some(metrics) = &self.0 {
333			metrics.unapproved_candidates_in_unfinalized_chain.set(count as u64);
334		}
335	}
336
337	pub fn observe_assignment_gathering_time(&self, stage: usize, elapsed_as_millis: usize) {
338		if let Some(metrics) = &self.0 {
339			let stage_string = stage.to_string();
340			// We don't want to have too many metrics entries with this label to not put unncessary
341			// pressure on the metrics infrastructure, so we cap the stage at 10, which is
342			// equivalent to having already a finalization lag to 10 * no_show_slots, so it should
343			// be more than enough.
344			metrics
345				.assignments_gathering_time_by_stage
346				.with_label_values(&[if stage < 10 { stage_string.as_str() } else { "inf" }])
347				.observe(elapsed_as_millis as f64);
348		}
349	}
350}
351
352impl metrics::Metrics for Metrics {
353	fn try_register(
354		registry: &prometheus::Registry,
355	) -> std::result::Result<Self, prometheus::PrometheusError> {
356		let metrics = MetricsInner {
357			imported_candidates_total: prometheus::register(
358				prometheus::Counter::new(
359					"polkadot_parachain_imported_candidates_total",
360					"Number of candidates imported by the approval voting subsystem",
361				)?,
362				registry,
363			)?,
364			assignments_produced: prometheus::register(
365				prometheus::Histogram::with_opts(
366					prometheus::HistogramOpts::new(
367						"polkadot_parachain_assignments_produced",
368						"Assignments and tranches produced by the approval voting subsystem",
369					).buckets(vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 10.0, 15.0, 25.0, 40.0, 70.0]),
370				)?,
371				registry,
372			)?,
373			approvals_produced_total: prometheus::register(
374				prometheus::CounterVec::new(
375					prometheus::Opts::new(
376						"polkadot_parachain_approvals_produced_total",
377						"Number of approvals produced by the approval voting subsystem",
378					),
379					&["status"]
380				)?,
381				registry,
382			)?,
383			no_shows_total: prometheus::register(
384				prometheus::Counter::new(
385					"polkadot_parachain_approvals_no_shows_total",
386					"Number of assignments which became no-shows in the approval voting subsystem",
387				)?,
388				registry,
389			)?,
390			observed_no_shows: prometheus::register(
391				prometheus::Counter::new(
392					"polkadot_parachain_approvals_observed_no_shows_total",
393					"Number of observed no shows at any moment in time",
394				)?,
395				registry,
396			)?,
397			wakeups_triggered_total: prometheus::register(
398				prometheus::Counter::new(
399					"polkadot_parachain_approvals_wakeups_total",
400					"Number of times we woke up to process a candidate in the approval voting subsystem",
401				)?,
402				registry,
403			)?,
404			candidate_approval_time_ticks: prometheus::register(
405				prometheus::Histogram::with_opts(
406					prometheus::HistogramOpts::new(
407						"polkadot_parachain_approvals_candidate_approval_time_ticks",
408						"Number of ticks (500ms) to approve candidates.",
409					).buckets(vec![6.0, 12.0, 18.0, 24.0, 30.0, 36.0, 72.0, 100.0, 144.0]),
410				)?,
411				registry,
412			)?,
413			coalesced_approvals_buckets: prometheus::register(
414				prometheus::Histogram::with_opts(
415					prometheus::HistogramOpts::new(
416						"polkadot_parachain_approvals_coalesced_approvals_buckets",
417						"Number of coalesced approvals.",
418					).buckets(vec![1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5]),
419				)?,
420				registry,
421			)?,
422			coalesced_approvals_delay: prometheus::register(
423				prometheus::Histogram::with_opts(
424					prometheus::HistogramOpts::new(
425						"polkadot_parachain_approvals_coalescing_delay",
426						"Number of ticks we delay the sending of a candidate approval",
427					).buckets(vec![1.1, 2.1, 3.1, 4.1, 6.1, 8.1, 12.1, 20.1, 32.1]),
428				)?,
429				registry,
430			)?,
431			approved_by_one_third: prometheus::register(
432				prometheus::Counter::new(
433					"polkadot_parachain_approved_by_one_third",
434					"Number of candidates where more than one third had to vote ",
435				)?,
436				registry,
437			)?,
438			block_approval_time_ticks: prometheus::register(
439				prometheus::Histogram::with_opts(
440					prometheus::HistogramOpts::new(
441						"polkadot_parachain_approvals_blockapproval_time_ticks",
442						"Number of ticks (500ms) to approve blocks.",
443					).buckets(vec![6.0, 12.0, 18.0, 24.0, 30.0, 36.0, 72.0, 100.0, 144.0]),
444				)?,
445				registry,
446			)?,
447			time_db_transaction: prometheus::register(
448				prometheus::Histogram::with_opts(
449					prometheus::HistogramOpts::new(
450						"polkadot_parachain_time_approval_db_transaction",
451						"Time spent writing an approval db transaction.",
452					)
453				)?,
454				registry,
455			)?,
456			time_recover_and_approve: prometheus::register(
457				prometheus::Histogram::with_opts(
458					prometheus::HistogramOpts::new(
459						"polkadot_parachain_time_recover_and_approve",
460						"Time spent recovering and approving data in approval voting",
461					)
462				)?,
463				registry,
464			)?,
465			candidate_signatures_requests_total: prometheus::register(
466				prometheus::Counter::new(
467					"polkadot_parachain_approval_candidate_signatures_requests_total",
468					"Number of times signatures got requested by other subsystems",
469				)?,
470				registry,
471			)?,
472			unapproved_candidates_in_unfinalized_chain: prometheus::register(
473				prometheus::Gauge::new(
474					"polkadot_parachain_approval_unapproved_candidates_in_unfinalized_chain",
475					"Number of unapproved candidates in unfinalized chain",
476				)?,
477				registry,
478			)?,
479			assignments_gathering_time_by_stage: prometheus::register(
480				prometheus::HistogramVec::new(
481					prometheus::HistogramOpts::new(
482						"polkadot_parachain_assignments_gather_time_by_stage_ms",
483						"The time in ms it takes for each stage to gather enough assignments needed for approval",
484					)
485					.buckets(vec![0.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0, 8000.0, 16000.0, 32000.0]),
486					&["stage"],
487				)?,
488				registry,
489			)?,
490		};
491
492		Ok(Metrics(Some(metrics)))
493	}
494}
495
496impl ApprovalVotingSubsystem {
497	/// Create a new approval voting subsystem with the given keystore, config, and database.
498	pub fn with_config(
499		config: Config,
500		db: Arc<dyn Database>,
501		keystore: Arc<LocalKeystore>,
502		sync_oracle: Box<dyn SyncOracle + Send>,
503		metrics: Metrics,
504		spawner: Arc<dyn overseer::gen::Spawner + 'static>,
505	) -> Self {
506		ApprovalVotingSubsystem::with_config_and_clock(
507			config,
508			db,
509			keystore,
510			sync_oracle,
511			metrics,
512			Arc::new(SystemClock {}),
513			spawner,
514			MAX_APPROVAL_RETRIES,
515			APPROVAL_CHECKING_TIMEOUT / 2,
516		)
517	}
518
519	/// Create a new approval voting subsystem with the given keystore, config, and database.
520	pub fn with_config_and_clock(
521		config: Config,
522		db: Arc<dyn Database>,
523		keystore: Arc<LocalKeystore>,
524		sync_oracle: Box<dyn SyncOracle + Send>,
525		metrics: Metrics,
526		clock: Arc<dyn Clock + Send + Sync>,
527		spawner: Arc<dyn overseer::gen::Spawner + 'static>,
528		max_approval_retries: u32,
529		retry_backoff: Duration,
530	) -> Self {
531		ApprovalVotingSubsystem {
532			keystore,
533			slot_duration_millis: config.slot_duration_millis,
534			db,
535			db_config: DatabaseConfig { col_approval_data: config.col_approval_data },
536			mode: Mode::Syncing(sync_oracle),
537			metrics,
538			clock,
539			spawner,
540			max_approval_retries,
541			retry_backoff,
542		}
543	}
544
545	/// Revert to the block corresponding to the specified `hash`.
546	/// The operation is not allowed for blocks older than the last finalized one.
547	pub fn revert_to(&self, hash: Hash) -> Result<(), SubsystemError> {
548		let config =
549			approval_db::common::Config { col_approval_data: self.db_config.col_approval_data };
550		let mut backend = approval_db::common::DbBackend::new(self.db.clone(), config);
551		let mut overlay = OverlayedBackend::new(&backend);
552
553		ops::revert_to(&mut overlay, hash)?;
554
555		let ops = overlay.into_write_ops();
556		backend.write(ops)
557	}
558}
559
560// Checks and logs approval vote db state. It is perfectly normal to start with an
561// empty approval vote DB if we changed DB type or the node will sync from scratch.
562fn db_sanity_check(db: Arc<dyn Database>, config: DatabaseConfig) -> SubsystemResult<()> {
563	let backend = DbBackend::new(db, config);
564	let all_blocks = backend.load_all_blocks()?;
565
566	if all_blocks.is_empty() {
567		gum::info!(target: LOG_TARGET, "Starting with an empty approval vote DB.",);
568	} else {
569		gum::debug!(
570			target: LOG_TARGET,
571			"Starting with {} blocks in approval vote DB.",
572			all_blocks.len()
573		);
574	}
575
576	Ok(())
577}
578
579#[overseer::subsystem(ApprovalVoting, error = SubsystemError, prefix = self::overseer)]
580impl<Context: Send> ApprovalVotingSubsystem {
581	fn start(self, mut ctx: Context) -> SpawnedSubsystem {
582		let backend = DbBackend::new(self.db.clone(), self.db_config);
583		let to_other_subsystems = ctx.sender().clone();
584		let to_approval_distr = ctx.sender().clone();
585
586		let future = run::<DbBackend, _, _, _>(
587			ctx,
588			to_other_subsystems,
589			to_approval_distr,
590			self,
591			Box::new(RealAssignmentCriteria),
592			backend,
593		)
594		.map_err(|e| SubsystemError::with_origin("approval-voting", e))
595		.boxed();
596
597		SpawnedSubsystem { name: "approval-voting-subsystem", future }
598	}
599}
600
601#[derive(Debug, Clone)]
602struct ApprovalVoteRequest {
603	validator_index: ValidatorIndex,
604	block_hash: Hash,
605}
606
607#[derive(Default)]
608struct Wakeups {
609	// Tick -> [(Relay Block, Candidate Hash)]
610	wakeups: BTreeMap<Tick, Vec<(Hash, CandidateHash)>>,
611	reverse_wakeups: HashMap<(Hash, CandidateHash), Tick>,
612	block_numbers: BTreeMap<BlockNumber, HashSet<Hash>>,
613}
614
615impl Wakeups {
616	// Returns the first tick there exist wakeups for, if any.
617	fn first(&self) -> Option<Tick> {
618		self.wakeups.keys().next().map(|t| *t)
619	}
620
621	fn note_block(&mut self, block_hash: Hash, block_number: BlockNumber) {
622		self.block_numbers.entry(block_number).or_default().insert(block_hash);
623	}
624
625	// Schedules a wakeup at the given tick. no-op if there is already an earlier or equal wake-up
626	// for these values. replaces any later wakeup.
627	fn schedule(
628		&mut self,
629		block_hash: Hash,
630		block_number: BlockNumber,
631		candidate_hash: CandidateHash,
632		tick: Tick,
633	) {
634		if let Some(prev) = self.reverse_wakeups.get(&(block_hash, candidate_hash)) {
635			if prev <= &tick {
636				return;
637			}
638
639			// we are replacing previous wakeup with an earlier one.
640			if let BTMEntry::Occupied(mut entry) = self.wakeups.entry(*prev) {
641				if let Some(pos) =
642					entry.get().iter().position(|x| x == &(block_hash, candidate_hash))
643				{
644					entry.get_mut().remove(pos);
645				}
646
647				if entry.get().is_empty() {
648					let _ = entry.remove_entry();
649				}
650			}
651		} else {
652			self.note_block(block_hash, block_number);
653		}
654
655		self.reverse_wakeups.insert((block_hash, candidate_hash), tick);
656		self.wakeups.entry(tick).or_default().push((block_hash, candidate_hash));
657	}
658
659	fn prune_finalized_wakeups(&mut self, finalized_number: BlockNumber) {
660		let after = self.block_numbers.split_off(&(finalized_number + 1));
661		let pruned_blocks: HashSet<_> = std::mem::replace(&mut self.block_numbers, after)
662			.into_iter()
663			.flat_map(|(_number, hashes)| hashes)
664			.collect();
665
666		let mut pruned_wakeups = BTreeMap::new();
667		self.reverse_wakeups.retain(|(h, c_h), tick| {
668			let live = !pruned_blocks.contains(h);
669			if !live {
670				pruned_wakeups.entry(*tick).or_insert_with(HashSet::new).insert((*h, *c_h));
671			}
672			live
673		});
674
675		for (tick, pruned) in pruned_wakeups {
676			if let BTMEntry::Occupied(mut entry) = self.wakeups.entry(tick) {
677				entry.get_mut().retain(|wakeup| !pruned.contains(wakeup));
678				if entry.get().is_empty() {
679					let _ = entry.remove();
680				}
681			}
682		}
683	}
684
685	// Get the wakeup for a particular block/candidate combo, if any.
686	fn wakeup_for(&self, block_hash: Hash, candidate_hash: CandidateHash) -> Option<Tick> {
687		self.reverse_wakeups.get(&(block_hash, candidate_hash)).map(|t| *t)
688	}
689
690	// Returns the next wakeup. this future never returns if there are no wakeups.
691	async fn next(&mut self, clock: &(dyn Clock + Sync)) -> (Tick, Hash, CandidateHash) {
692		match self.first() {
693			None => future::pending().await,
694			Some(tick) => {
695				clock.wait(tick).await;
696				match self.wakeups.entry(tick) {
697					BTMEntry::Vacant(_) => {
698						panic!("entry is known to exist since `first` was `Some`; qed")
699					},
700					BTMEntry::Occupied(mut entry) => {
701						let (hash, candidate_hash) = entry.get_mut().pop()
702							.expect("empty entries are removed here and in `schedule`; no other mutation of this map; qed");
703
704						if entry.get().is_empty() {
705							let _ = entry.remove();
706						}
707
708						self.reverse_wakeups.remove(&(hash, candidate_hash));
709
710						(tick, hash, candidate_hash)
711					},
712				}
713			},
714		}
715	}
716}
717
718struct ApprovalStatus {
719	required_tranches: RequiredTranches,
720	tranche_now: DelayTranche,
721	block_tick: Tick,
722	last_no_shows: usize,
723	no_show_validators: Vec<ValidatorIndex>,
724}
725
726#[derive(Copy, Clone)]
727enum ApprovalOutcome {
728	Approved,
729	Failed,
730	TimedOut,
731}
732
733#[derive(Clone)]
734struct RetryApprovalInfo {
735	candidate: CandidateReceipt,
736	backing_group: GroupIndex,
737	core_index: Option<CoreIndex>,
738	session_index: SessionIndex,
739	attempts_remaining: u32,
740	backoff: Duration,
741}
742
743struct ApprovalState {
744	validator_index: ValidatorIndex,
745	candidate_hash: CandidateHash,
746	approval_outcome: ApprovalOutcome,
747	retry_info: Option<RetryApprovalInfo>,
748}
749
750impl ApprovalState {
751	fn approved(validator_index: ValidatorIndex, candidate_hash: CandidateHash) -> Self {
752		Self {
753			validator_index,
754			candidate_hash,
755			approval_outcome: ApprovalOutcome::Approved,
756			retry_info: None,
757		}
758	}
759	fn failed(validator_index: ValidatorIndex, candidate_hash: CandidateHash) -> Self {
760		Self {
761			validator_index,
762			candidate_hash,
763			approval_outcome: ApprovalOutcome::Failed,
764			retry_info: None,
765		}
766	}
767
768	fn failed_with_retry(
769		validator_index: ValidatorIndex,
770		candidate_hash: CandidateHash,
771		retry_info: Option<RetryApprovalInfo>,
772	) -> Self {
773		Self {
774			validator_index,
775			candidate_hash,
776			approval_outcome: ApprovalOutcome::Failed,
777			retry_info,
778		}
779	}
780}
781
782struct CurrentlyCheckingSet {
783	candidate_hash_map: HashMap<CandidateHash, HashSet<Hash>>,
784	currently_checking: FuturesUnordered<BoxFuture<'static, ApprovalState>>,
785}
786
787impl Default for CurrentlyCheckingSet {
788	fn default() -> Self {
789		Self { candidate_hash_map: HashMap::new(), currently_checking: FuturesUnordered::new() }
790	}
791}
792
793impl CurrentlyCheckingSet {
794	// This function will lazily launch approval voting work whenever the
795	// candidate is not already undergoing validation.
796	pub async fn insert_relay_block_hash(
797		&mut self,
798		candidate_hash: CandidateHash,
799		validator_index: ValidatorIndex,
800		relay_block: Hash,
801		launch_work: impl Future<Output = SubsystemResult<RemoteHandle<ApprovalState>>>,
802	) -> SubsystemResult<()> {
803		match self.candidate_hash_map.entry(candidate_hash) {
804			HMEntry::Occupied(mut entry) => {
805				// validation already undergoing. just add the relay hash if unknown.
806				entry.get_mut().insert(relay_block);
807			},
808			HMEntry::Vacant(entry) => {
809				// validation not ongoing. launch work and time out the remote handle.
810				entry.insert(HashSet::new()).insert(relay_block);
811				let work = launch_work.await?;
812				self.currently_checking.push(Box::pin(async move {
813					match work.timeout(APPROVAL_CHECKING_TIMEOUT).await {
814						None => ApprovalState {
815							candidate_hash,
816							validator_index,
817							approval_outcome: ApprovalOutcome::TimedOut,
818							retry_info: None,
819						},
820						Some(approval_state) => approval_state,
821					}
822				}));
823			},
824		}
825
826		Ok(())
827	}
828
829	pub async fn next(
830		&mut self,
831		approvals_cache: &mut LruMap<CandidateHash, ApprovalOutcome>,
832	) -> (HashSet<Hash>, ApprovalState) {
833		if !self.currently_checking.is_empty() {
834			if let Some(approval_state) = self.currently_checking.next().await {
835				let out = self
836					.candidate_hash_map
837					.remove(&approval_state.candidate_hash)
838					.unwrap_or_default();
839				approvals_cache
840					.insert(approval_state.candidate_hash, approval_state.approval_outcome);
841				return (out, approval_state);
842			}
843		}
844
845		future::pending().await
846	}
847}
848
849async fn get_extended_session_info_by_index<'a, Sender>(
850	runtime_info: &'a mut RuntimeInfo,
851	sender: &mut Sender,
852	block_hash: Hash,
853	session_index: SessionIndex,
854) -> Option<&'a ExtendedSessionInfo>
855where
856	Sender: SubsystemSender<RuntimeApiMessage>,
857{
858	match runtime_info.get_session_info_by_index(sender, block_hash, session_index).await {
859		Ok(extended_info) => Some(&extended_info),
860		Err(_) => {
861			gum::debug!(
862				target: LOG_TARGET,
863				session = session_index,
864				?block_hash,
865				"Can't obtain SessionInfo or ExecutorParams"
866			);
867			None
868		},
869	}
870}
871
872async fn get_session_info_by_index<'a, Sender>(
873	runtime_info: &'a mut RuntimeInfo,
874	sender: &mut Sender,
875	block_hash: Hash,
876	session_index: SessionIndex,
877) -> Option<&'a SessionInfo>
878where
879	Sender: SubsystemSender<RuntimeApiMessage>,
880{
881	get_extended_session_info_by_index(runtime_info, sender, block_hash, session_index)
882		.await
883		.map(|extended_info| &extended_info.session_info)
884}
885
886struct State {
887	keystore: Arc<LocalKeystore>,
888	slot_duration_millis: u64,
889	clock: Arc<dyn Clock + Send + Sync>,
890	assignment_criteria: Box<dyn AssignmentCriteria + Send + Sync>,
891	// Per block, candidate records about how long we take until we gather enough
892	// assignments, this is relevant because it gives us a good idea about how many
893	// tranches we trigger and why.
894	per_block_assignments_gathering_times:
895		LruMap<BlockNumber, HashMap<(Hash, CandidateHash), AssignmentGatheringRecord>>,
896	no_show_stats: NoShowStats,
897}
898
899// Regularly dump the no-show stats at this block number frequency.
900const NO_SHOW_DUMP_FREQUENCY: BlockNumber = 50;
901// The maximum number of validators we record no-shows for, per candidate.
902pub(crate) const MAX_RECORDED_NO_SHOW_VALIDATORS_PER_CANDIDATE: usize = 20;
903
904// No show stats per validator and per parachain.
905// This is valuable information when we have to debug live network issue, because
906// it gives information if things are going wrong only for some validators or just
907// for some parachains.
908#[derive(Debug, Clone, PartialEq, Eq, Default)]
909struct NoShowStats {
910	per_validator_no_show: HashMap<SessionIndex, HashMap<ValidatorIndex, usize>>,
911	per_parachain_no_show: HashMap<u32, usize>,
912	last_dumped_block_number: BlockNumber,
913}
914
915impl NoShowStats {
916	// Print the no-show stats if NO_SHOW_DUMP_FREQUENCY blocks have passed since the last
917	// print.
918	fn maybe_print(&mut self, current_block_number: BlockNumber) {
919		if self.last_dumped_block_number > current_block_number ||
920			current_block_number - self.last_dumped_block_number < NO_SHOW_DUMP_FREQUENCY
921		{
922			return;
923		}
924		if self.per_parachain_no_show.is_empty() && self.per_validator_no_show.is_empty() {
925			return;
926		}
927
928		gum::debug!(
929			target: LOG_TARGET,
930			"Validators with no_show {:?} and parachains with no_shows {:?} since {:}",
931			self.per_validator_no_show,
932			self.per_parachain_no_show,
933			self.last_dumped_block_number
934		);
935
936		self.last_dumped_block_number = current_block_number;
937
938		self.per_validator_no_show.clear();
939		self.per_parachain_no_show.clear();
940	}
941}
942
943#[derive(Debug, Clone, PartialEq, Eq)]
944struct AssignmentGatheringRecord {
945	// The stage we are in.
946	// Candidate assignment gathering goes in stages, first we wait for needed_approvals(stage 0)
947	// Then if we have no-shows, we move into stage 1 and wait for enough tranches to cover all
948	// no-shows.
949	stage: usize,
950	// The time we started the stage.
951	stage_start: Option<Instant>,
952}
953
954impl Default for AssignmentGatheringRecord {
955	fn default() -> Self {
956		AssignmentGatheringRecord { stage: 0, stage_start: Some(Instant::now()) }
957	}
958}
959
960#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)]
961impl State {
962	// Compute the required tranches for approval for this block and candidate combo.
963	// Fails if there is no approval entry for the block under the candidate or no candidate entry
964	// under the block, or if the session is out of bounds.
965	async fn approval_status<Sender, 'a, 'b>(
966		&'a self,
967		sender: &mut Sender,
968		session_info_provider: &'a mut RuntimeInfo,
969		block_entry: &'a BlockEntry,
970		candidate_entry: &'b CandidateEntry,
971	) -> Option<(&'b ApprovalEntry, ApprovalStatus)>
972	where
973		Sender: SubsystemSender<RuntimeApiMessage>,
974	{
975		let session_info = match get_session_info_by_index(
976			session_info_provider,
977			sender,
978			block_entry.parent_hash(),
979			block_entry.session(),
980		)
981		.await
982		{
983			Some(s) => s,
984			None => return None,
985		};
986		let block_hash = block_entry.block_hash();
987
988		let tranche_now = self.clock.tranche_now(self.slot_duration_millis, block_entry.slot());
989		let block_tick = slot_number_to_tick(self.slot_duration_millis, block_entry.slot());
990		let no_show_duration = slot_number_to_tick(
991			self.slot_duration_millis,
992			Slot::from(u64::from(session_info.no_show_slots)),
993		);
994
995		if let Some(approval_entry) = candidate_entry.approval_entry(&block_hash) {
996			let TranchesToApproveResult {
997				required_tranches,
998				total_observed_no_shows,
999				no_show_validators,
1000			} = approval_checking::tranches_to_approve(
1001				approval_entry,
1002				candidate_entry.approvals(),
1003				tranche_now,
1004				block_tick,
1005				no_show_duration,
1006				session_info.needed_approvals as _,
1007			);
1008
1009			let status = ApprovalStatus {
1010				required_tranches,
1011				block_tick,
1012				tranche_now,
1013				last_no_shows: total_observed_no_shows,
1014				no_show_validators,
1015			};
1016
1017			Some((approval_entry, status))
1018		} else {
1019			None
1020		}
1021	}
1022
1023	fn mark_begining_of_gathering_assignments(
1024		&mut self,
1025		block_number: BlockNumber,
1026		block_hash: Hash,
1027		candidate: CandidateHash,
1028	) {
1029		if let Some(record) = self
1030			.per_block_assignments_gathering_times
1031			.get_or_insert(block_number, HashMap::new)
1032			.and_then(|records| Some(records.entry((block_hash, candidate)).or_default()))
1033		{
1034			if record.stage_start.is_none() {
1035				record.stage += 1;
1036				gum::debug!(
1037					target: LOG_TARGET,
1038					stage = ?record.stage,
1039					?block_hash,
1040					?candidate,
1041					"Started a new assignment gathering stage",
1042				);
1043				record.stage_start = Some(Instant::now());
1044			}
1045		}
1046	}
1047
1048	fn mark_gathered_enough_assignments(
1049		&mut self,
1050		block_number: BlockNumber,
1051		block_hash: Hash,
1052		candidate: CandidateHash,
1053	) -> AssignmentGatheringRecord {
1054		let record = self
1055			.per_block_assignments_gathering_times
1056			.get(&block_number)
1057			.and_then(|entry| entry.get_mut(&(block_hash, candidate)));
1058		let stage = record.as_ref().map(|record| record.stage).unwrap_or_default();
1059		AssignmentGatheringRecord {
1060			stage,
1061			stage_start: record.and_then(|record| record.stage_start.take()),
1062		}
1063	}
1064
1065	fn cleanup_assignments_gathering_timestamp(&mut self, remove_lower_than: BlockNumber) {
1066		while let Some((block_number, _)) = self.per_block_assignments_gathering_times.peek_oldest()
1067		{
1068			if *block_number < remove_lower_than {
1069				self.per_block_assignments_gathering_times.pop_oldest();
1070			} else {
1071				break;
1072			}
1073		}
1074	}
1075
1076	fn observe_assignment_gathering_status(
1077		&mut self,
1078		metrics: &Metrics,
1079		required_tranches: &RequiredTranches,
1080		block_hash: Hash,
1081		block_number: BlockNumber,
1082		candidate_hash: CandidateHash,
1083	) {
1084		match required_tranches {
1085			RequiredTranches::All | RequiredTranches::Pending { .. } => {
1086				self.mark_begining_of_gathering_assignments(
1087					block_number,
1088					block_hash,
1089					candidate_hash,
1090				);
1091			},
1092			RequiredTranches::Exact { .. } => {
1093				let time_to_gather =
1094					self.mark_gathered_enough_assignments(block_number, block_hash, candidate_hash);
1095				if let Some(gathering_started) = time_to_gather.stage_start {
1096					if gathering_started.elapsed().as_millis() > 6000 {
1097						gum::trace!(
1098							target: LOG_TARGET,
1099							?block_hash,
1100							?candidate_hash,
1101							"Long assignment gathering time",
1102						);
1103					}
1104					metrics.observe_assignment_gathering_time(
1105						time_to_gather.stage,
1106						gathering_started.elapsed().as_millis() as usize,
1107					)
1108				}
1109			},
1110		}
1111	}
1112
1113	fn record_no_shows(
1114		&mut self,
1115		session_index: SessionIndex,
1116		para_id: u32,
1117		no_show_validators: &Vec<ValidatorIndex>,
1118	) {
1119		if !no_show_validators.is_empty() {
1120			*self.no_show_stats.per_parachain_no_show.entry(para_id.into()).or_default() += 1;
1121		}
1122		for validator_index in no_show_validators {
1123			*self
1124				.no_show_stats
1125				.per_validator_no_show
1126				.entry(session_index)
1127				.or_default()
1128				.entry(*validator_index)
1129				.or_default() += 1;
1130		}
1131	}
1132}
1133
1134#[derive(Debug, Clone)]
1135enum Action {
1136	ScheduleWakeup {
1137		block_hash: Hash,
1138		block_number: BlockNumber,
1139		candidate_hash: CandidateHash,
1140		tick: Tick,
1141	},
1142	LaunchApproval {
1143		claimed_candidate_indices: CandidateBitfield,
1144		candidate_hash: CandidateHash,
1145		indirect_cert: IndirectAssignmentCertV2,
1146		assignment_tranche: DelayTranche,
1147		relay_block_hash: Hash,
1148		session: SessionIndex,
1149		candidate: CandidateReceipt,
1150		backing_group: GroupIndex,
1151		distribute_assignment: bool,
1152		core_index: Option<CoreIndex>,
1153	},
1154	NoteApprovedInChainSelection(Hash),
1155	IssueApproval(CandidateHash, ApprovalVoteRequest),
1156	BecomeActive,
1157	Conclude,
1158}
1159
1160/// Trait for providing approval voting subsystem with work.
1161#[async_trait::async_trait]
1162pub trait ApprovalVotingWorkProvider {
1163	async fn recv(&mut self) -> SubsystemResult<FromOrchestra<ApprovalVotingMessage>>;
1164}
1165
1166#[async_trait::async_trait]
1167#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)]
1168impl<Context> ApprovalVotingWorkProvider for Context {
1169	async fn recv(&mut self) -> SubsystemResult<FromOrchestra<ApprovalVotingMessage>> {
1170		self.recv().await
1171	}
1172}
1173
1174#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)]
1175async fn run<
1176	B,
1177	WorkProvider: ApprovalVotingWorkProvider,
1178	Sender: SubsystemSender<ChainApiMessage>
1179		+ SubsystemSender<RuntimeApiMessage>
1180		+ SubsystemSender<ChainSelectionMessage>
1181		+ SubsystemSender<AvailabilityRecoveryMessage>
1182		+ SubsystemSender<DisputeCoordinatorMessage>
1183		+ SubsystemSender<CandidateValidationMessage>
1184		+ Clone,
1185	ADSender: SubsystemSender<ApprovalDistributionMessage>,
1186>(
1187	mut work_provider: WorkProvider,
1188	mut to_other_subsystems: Sender,
1189	mut to_approval_distr: ADSender,
1190	mut subsystem: ApprovalVotingSubsystem,
1191	assignment_criteria: Box<dyn AssignmentCriteria + Send + Sync>,
1192	mut backend: B,
1193) -> SubsystemResult<()>
1194where
1195	B: Backend,
1196{
1197	if let Err(err) = db_sanity_check(subsystem.db.clone(), subsystem.db_config) {
1198		gum::warn!(target: LOG_TARGET, ?err, "Could not run approval vote DB sanity check");
1199	}
1200
1201	let mut state = State {
1202		keystore: subsystem.keystore,
1203		slot_duration_millis: subsystem.slot_duration_millis,
1204		clock: subsystem.clock,
1205		assignment_criteria,
1206		per_block_assignments_gathering_times: LruMap::new(ByLength::new(
1207			MAX_BLOCKS_WITH_ASSIGNMENT_TIMESTAMPS,
1208		)),
1209		no_show_stats: NoShowStats::default(),
1210	};
1211
1212	let mut last_finalized_height: Option<BlockNumber> = {
1213		let (tx, rx) = oneshot::channel();
1214		to_other_subsystems
1215			.send_message(ChainApiMessage::FinalizedBlockNumber(tx))
1216			.await;
1217		match rx.await? {
1218			Ok(number) => Some(number),
1219			Err(err) => {
1220				gum::warn!(target: LOG_TARGET, ?err, "Failed fetching finalized number");
1221				None
1222			},
1223		}
1224	};
1225
1226	// `None` on start-up. Gets initialized/updated on leaf update
1227	let mut session_info_provider = RuntimeInfo::new_with_config(RuntimeInfoConfig {
1228		keystore: None,
1229		session_cache_lru_size: DISPUTE_WINDOW.get(),
1230	});
1231
1232	let mut wakeups = Wakeups::default();
1233	let mut currently_checking_set = CurrentlyCheckingSet::default();
1234	let mut delayed_approvals_timers = DelayedApprovalTimer::default();
1235	let mut approvals_cache = LruMap::new(ByLength::new(APPROVAL_CACHE_SIZE));
1236
1237	loop {
1238		let mut overlayed_db = OverlayedBackend::new(&backend);
1239		let actions = futures::select! {
1240			(_tick, woken_block, woken_candidate) = wakeups.next(&*state.clock).fuse() => {
1241				subsystem.metrics.on_wakeup();
1242				process_wakeup(
1243					&mut to_other_subsystems,
1244					&mut state,
1245					&mut overlayed_db,
1246					&mut session_info_provider,
1247					woken_block,
1248					woken_candidate,
1249					&subsystem.metrics,
1250					&wakeups,
1251				).await?
1252			}
1253			next_msg = work_provider.recv().fuse() => {
1254				let mut actions = handle_from_overseer(
1255					&mut to_other_subsystems,
1256					&mut to_approval_distr,
1257					&subsystem.spawner,
1258					&mut state,
1259					&mut overlayed_db,
1260					&mut session_info_provider,
1261					&subsystem.metrics,
1262					next_msg?,
1263					&mut last_finalized_height,
1264					&mut wakeups,
1265				).await?;
1266
1267				if let Mode::Syncing(ref mut oracle) = subsystem.mode {
1268					if !oracle.is_major_syncing() {
1269						// note that we're active before processing other actions.
1270						actions.insert(0, Action::BecomeActive)
1271					}
1272				}
1273
1274				actions
1275			}
1276			approval_state = currently_checking_set.next(&mut approvals_cache).fuse() => {
1277				let mut actions = Vec::new();
1278				let (
1279					relay_block_hashes,
1280					ApprovalState {
1281						validator_index,
1282						candidate_hash,
1283						approval_outcome,
1284						retry_info,
1285					}
1286				) = approval_state;
1287
1288				if matches!(approval_outcome, ApprovalOutcome::Approved) {
1289					let mut approvals: Vec<Action> = relay_block_hashes
1290						.iter()
1291						.map(|block_hash|
1292							Action::IssueApproval(
1293								candidate_hash,
1294								ApprovalVoteRequest {
1295									validator_index,
1296									block_hash: *block_hash,
1297								},
1298							)
1299						)
1300						.collect();
1301					actions.append(&mut approvals);
1302				}
1303
1304				if let Some(retry_info) = retry_info {
1305					for block_hash in relay_block_hashes {
1306						if overlayed_db.load_block_entry(&block_hash).map(|block_info| block_info.is_some()).unwrap_or(false) {
1307							let sender = to_other_subsystems.clone();
1308							let spawn_handle = subsystem.spawner.clone();
1309							let metrics = subsystem.metrics.clone();
1310							let retry_info = retry_info.clone();
1311							let candidate = retry_info.candidate.clone();
1312
1313							currently_checking_set
1314								.insert_relay_block_hash(
1315									candidate_hash,
1316									validator_index,
1317									block_hash,
1318									async move {
1319										launch_approval(
1320											sender,
1321											spawn_handle,
1322											metrics,
1323											retry_info.session_index,
1324											candidate,
1325											validator_index,
1326											block_hash,
1327											retry_info.backing_group,
1328											retry_info.core_index,
1329											retry_info,
1330										)
1331										.await
1332									},
1333								)
1334								.await?;
1335						}
1336					}
1337				}
1338
1339				actions
1340			},
1341			(block_hash, validator_index) = delayed_approvals_timers.select_next_some() => {
1342				gum::debug!(
1343					target: LOG_TARGET,
1344					?block_hash,
1345					?validator_index,
1346					"Sign approval for multiple candidates",
1347				);
1348
1349				match maybe_create_signature(
1350					&mut overlayed_db,
1351					&mut session_info_provider,
1352					&state,
1353					&mut to_other_subsystems,
1354					&mut to_approval_distr,
1355					block_hash,
1356					validator_index,
1357					&subsystem.metrics,
1358				).await {
1359					Ok(Some(next_wakeup)) => {
1360						delayed_approvals_timers.maybe_arm_timer(next_wakeup, state.clock.as_ref(), block_hash, validator_index);
1361					},
1362					Ok(None) => {}
1363					Err(err) => {
1364						gum::error!(
1365							target: LOG_TARGET,
1366							?err,
1367							"Failed to create signature",
1368						);
1369					}
1370				}
1371				vec![]
1372			}
1373		};
1374
1375		if handle_actions(
1376			&mut to_other_subsystems,
1377			&mut to_approval_distr,
1378			&subsystem.spawner,
1379			&mut state,
1380			&mut overlayed_db,
1381			&mut session_info_provider,
1382			&subsystem.metrics,
1383			&mut wakeups,
1384			&mut currently_checking_set,
1385			&mut delayed_approvals_timers,
1386			&mut approvals_cache,
1387			&mut subsystem.mode,
1388			actions,
1389			subsystem.max_approval_retries,
1390			subsystem.retry_backoff,
1391		)
1392		.await?
1393		{
1394			break;
1395		}
1396
1397		if !overlayed_db.is_empty() {
1398			let _timer = subsystem.metrics.time_db_transaction();
1399			let ops = overlayed_db.into_write_ops();
1400			backend.write(ops)?;
1401		}
1402	}
1403
1404	Ok(())
1405}
1406
1407// Starts a worker thread that runs the approval voting subsystem.
1408pub async fn start_approval_worker<
1409	WorkProvider: ApprovalVotingWorkProvider + Send + 'static,
1410	Sender: SubsystemSender<ChainApiMessage>
1411		+ SubsystemSender<RuntimeApiMessage>
1412		+ SubsystemSender<ChainSelectionMessage>
1413		+ SubsystemSender<AvailabilityRecoveryMessage>
1414		+ SubsystemSender<DisputeCoordinatorMessage>
1415		+ SubsystemSender<CandidateValidationMessage>
1416		+ Clone,
1417	ADSender: SubsystemSender<ApprovalDistributionMessage>,
1418>(
1419	work_provider: WorkProvider,
1420	to_other_subsystems: Sender,
1421	to_approval_distr: ADSender,
1422	config: Config,
1423	db: Arc<dyn Database>,
1424	keystore: Arc<LocalKeystore>,
1425	sync_oracle: Box<dyn SyncOracle + Send>,
1426	metrics: Metrics,
1427	spawner: Arc<dyn overseer::gen::Spawner + 'static>,
1428	task_name: &'static str,
1429	group_name: &'static str,
1430	clock: Arc<dyn Clock + Send + Sync>,
1431) -> SubsystemResult<()> {
1432	let approval_voting = ApprovalVotingSubsystem::with_config_and_clock(
1433		config,
1434		db.clone(),
1435		keystore,
1436		sync_oracle,
1437		metrics,
1438		clock,
1439		spawner,
1440		MAX_APPROVAL_RETRIES,
1441		APPROVAL_CHECKING_TIMEOUT / 2,
1442	);
1443	let backend = DbBackend::new(db.clone(), approval_voting.db_config);
1444	let spawner = approval_voting.spawner.clone();
1445	spawner.spawn_blocking(
1446		task_name,
1447		Some(group_name),
1448		Box::pin(async move {
1449			if let Err(err) = run(
1450				work_provider,
1451				to_other_subsystems,
1452				to_approval_distr,
1453				approval_voting,
1454				Box::new(RealAssignmentCriteria),
1455				backend,
1456			)
1457			.await
1458			{
1459				gum::error!(target: LOG_TARGET, ?err, "Approval voting worker stopped processing messages");
1460			};
1461		}),
1462	);
1463	Ok(())
1464}
1465
1466// Handle actions is a function that accepts a set of instructions
1467// and subsequently updates the underlying approvals_db in accordance
1468// with the linear set of instructions passed in. Therefore, actions
1469// must be processed in series to ensure that earlier actions are not
1470// negated/corrupted by later actions being executed out-of-order.
1471//
1472// However, certain Actions can cause additional actions to need to be
1473// processed by this function. In order to preserve linearity, we would
1474// need to handle these newly generated actions before we finalize
1475// completing additional actions in the submitted sequence of actions.
1476//
1477// Since recursive async functions are not stable yet, we are
1478// forced to modify the actions iterator on the fly whenever a new set
1479// of actions are generated by handling a single action.
1480//
1481// This particular problem statement is specified in issue 3311:
1482// 	https://github.com/paritytech/polkadot/issues/3311
1483//
1484// returns `true` if any of the actions was a `Conclude` command.
1485#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)]
1486async fn handle_actions<
1487	Sender: SubsystemSender<ChainApiMessage>
1488		+ SubsystemSender<RuntimeApiMessage>
1489		+ SubsystemSender<ChainSelectionMessage>
1490		+ SubsystemSender<AvailabilityRecoveryMessage>
1491		+ SubsystemSender<DisputeCoordinatorMessage>
1492		+ SubsystemSender<CandidateValidationMessage>
1493		+ Clone,
1494	ADSender: SubsystemSender<ApprovalDistributionMessage>,
1495>(
1496	sender: &mut Sender,
1497	approval_voting_sender: &mut ADSender,
1498	spawn_handle: &Arc<dyn overseer::gen::Spawner + 'static>,
1499	state: &mut State,
1500	overlayed_db: &mut OverlayedBackend<'_, impl Backend>,
1501	session_info_provider: &mut RuntimeInfo,
1502	metrics: &Metrics,
1503	wakeups: &mut Wakeups,
1504	currently_checking_set: &mut CurrentlyCheckingSet,
1505	delayed_approvals_timers: &mut DelayedApprovalTimer,
1506	approvals_cache: &mut LruMap<CandidateHash, ApprovalOutcome>,
1507	mode: &mut Mode,
1508	actions: Vec<Action>,
1509	max_approval_retries: u32,
1510	retry_backoff: Duration,
1511) -> SubsystemResult<bool> {
1512	let mut conclude = false;
1513	let mut actions_iter = actions.into_iter();
1514	while let Some(action) = actions_iter.next() {
1515		match action {
1516			Action::ScheduleWakeup { block_hash, block_number, candidate_hash, tick } => {
1517				wakeups.schedule(block_hash, block_number, candidate_hash, tick);
1518			},
1519			Action::IssueApproval(candidate_hash, approval_request) => {
1520				// Note that the IssueApproval action will create additional
1521				// actions that will need to all be processed before we can
1522				// handle the next action in the set passed to the ambient
1523				// function.
1524				//
1525				// In order to achieve this, we append the existing iterator
1526				// to the end of the iterator made up of these newly generated
1527				// actions.
1528				//
1529				// Note that chaining these iterators is O(n) as we must consume
1530				// the prior iterator.
1531				let next_actions: Vec<Action> = issue_approval(
1532					sender,
1533					approval_voting_sender,
1534					state,
1535					overlayed_db,
1536					session_info_provider,
1537					metrics,
1538					candidate_hash,
1539					delayed_approvals_timers,
1540					approval_request,
1541					&wakeups,
1542				)
1543				.await?
1544				.into_iter()
1545				.map(|v| v.clone())
1546				.chain(actions_iter)
1547				.collect();
1548
1549				actions_iter = next_actions.into_iter();
1550			},
1551			Action::LaunchApproval {
1552				claimed_candidate_indices,
1553				candidate_hash,
1554				indirect_cert,
1555				assignment_tranche,
1556				relay_block_hash,
1557				session,
1558				candidate,
1559				backing_group,
1560				distribute_assignment,
1561				core_index,
1562			} => {
1563				// Don't launch approval work if the node is syncing.
1564				if let Mode::Syncing(_) = *mode {
1565					continue;
1566				}
1567
1568				metrics.on_assignment_produced(assignment_tranche);
1569				let block_hash = indirect_cert.block_hash;
1570				let validator_index = indirect_cert.validator;
1571
1572				if distribute_assignment {
1573					approval_voting_sender.send_unbounded_message(
1574						ApprovalDistributionMessage::DistributeAssignment(
1575							indirect_cert,
1576							claimed_candidate_indices,
1577						),
1578					);
1579				}
1580
1581				match approvals_cache.get(&candidate_hash) {
1582					Some(ApprovalOutcome::Approved) => {
1583						let new_actions: Vec<Action> = std::iter::once(Action::IssueApproval(
1584							candidate_hash,
1585							ApprovalVoteRequest { validator_index, block_hash },
1586						))
1587						.map(|v| v.clone())
1588						.chain(actions_iter)
1589						.collect();
1590						actions_iter = new_actions.into_iter();
1591					},
1592					None => {
1593						let sender = sender.clone();
1594						let spawn_handle = spawn_handle.clone();
1595
1596						let retry = RetryApprovalInfo {
1597							candidate: candidate.clone(),
1598							backing_group,
1599							core_index,
1600							session_index: session,
1601							attempts_remaining: max_approval_retries,
1602							backoff: retry_backoff,
1603						};
1604
1605						currently_checking_set
1606							.insert_relay_block_hash(
1607								candidate_hash,
1608								validator_index,
1609								relay_block_hash,
1610								async move {
1611									launch_approval(
1612										sender,
1613										spawn_handle,
1614										metrics.clone(),
1615										session,
1616										candidate,
1617										validator_index,
1618										block_hash,
1619										backing_group,
1620										core_index,
1621										retry,
1622									)
1623									.await
1624								},
1625							)
1626							.await?;
1627					},
1628					Some(_) => {},
1629				}
1630			},
1631			Action::NoteApprovedInChainSelection(block_hash) => {
1632				sender.send_message(ChainSelectionMessage::Approved(block_hash)).await;
1633			},
1634			Action::BecomeActive => {
1635				*mode = Mode::Active;
1636
1637				let (messages, next_actions) = distribution_messages_for_activation(
1638					overlayed_db,
1639					state,
1640					delayed_approvals_timers,
1641				)
1642				.await?;
1643				for message in messages.into_iter() {
1644					approval_voting_sender.send_unbounded_message(message);
1645				}
1646				let next_actions: Vec<Action> =
1647					next_actions.into_iter().map(|v| v.clone()).chain(actions_iter).collect();
1648
1649				actions_iter = next_actions.into_iter();
1650			},
1651			Action::Conclude => {
1652				conclude = true;
1653			},
1654		}
1655	}
1656
1657	Ok(conclude)
1658}
1659
1660fn cores_to_candidate_indices(
1661	core_indices: &CoreBitfield,
1662	block_entry: &BlockEntry,
1663) -> Result<CandidateBitfield, BitfieldError> {
1664	let mut candidate_indices = Vec::new();
1665
1666	// Map from core index to candidate index.
1667	for claimed_core_index in core_indices.iter_ones() {
1668		if let Some(candidate_index) = block_entry
1669			.candidates()
1670			.iter()
1671			.position(|(core_index, _)| core_index.0 == claimed_core_index as u32)
1672		{
1673			candidate_indices.push(candidate_index as _);
1674		}
1675	}
1676
1677	CandidateBitfield::try_from(candidate_indices)
1678}
1679
1680// Returns the claimed core bitfield from the assignment cert.
1681fn get_assignment_core_indices(assignment: &AssignmentCertKindV2) -> CoreBitfield {
1682	match &assignment {
1683		AssignmentCertKindV2::RelayVRFModuloCompact { core_bitfield } => core_bitfield.clone(),
1684		AssignmentCertKindV2::RelayVRFDelay { core_index } => {
1685			CoreBitfield::try_from(vec![*core_index]).expect("Not an empty vec; qed")
1686		},
1687	}
1688}
1689
1690async fn distribution_messages_for_activation(
1691	db: &OverlayedBackend<'_, impl Backend>,
1692	state: &State,
1693	delayed_approvals_timers: &mut DelayedApprovalTimer,
1694) -> SubsystemResult<(Vec<ApprovalDistributionMessage>, Vec<Action>)> {
1695	let all_blocks: Vec<Hash> = db.load_all_blocks()?;
1696
1697	let mut approval_meta = Vec::with_capacity(all_blocks.len());
1698	let mut messages = Vec::new();
1699	let mut approvals = Vec::new();
1700	let mut actions = Vec::new();
1701
1702	messages.push(ApprovalDistributionMessage::NewBlocks(Vec::new())); // dummy value.
1703
1704	for block_hash in all_blocks {
1705		let block_entry = match db.load_block_entry(&block_hash)? {
1706			Some(b) => b,
1707			None => {
1708				gum::warn!(target: LOG_TARGET, ?block_hash, "Missing block entry");
1709
1710				continue;
1711			},
1712		};
1713
1714		approval_meta.push(BlockApprovalMeta {
1715			hash: block_hash,
1716			number: block_entry.block_number(),
1717			parent_hash: block_entry.parent_hash(),
1718			candidates: block_entry
1719				.candidates()
1720				.iter()
1721				.map(|(core_index, c_hash)| {
1722					let candidate = db.load_candidate_entry(c_hash).ok().flatten();
1723					let group_index = candidate
1724						.and_then(|entry| {
1725							entry.approval_entry(&block_hash).map(|entry| entry.backing_group())
1726						})
1727						.unwrap_or_else(|| {
1728							gum::warn!(
1729								target: LOG_TARGET,
1730								?block_hash,
1731								?c_hash,
1732								"Missing candidate entry or approval entry",
1733							);
1734							GroupIndex::default()
1735						});
1736					(*c_hash, *core_index, group_index)
1737				})
1738				.collect(),
1739			slot: block_entry.slot(),
1740			session: block_entry.session(),
1741			vrf_story: block_entry.relay_vrf_story(),
1742		});
1743		let mut signatures_queued = HashSet::new();
1744		for (core_index, candidate_hash) in block_entry.candidates() {
1745			let candidate_entry = match db.load_candidate_entry(&candidate_hash)? {
1746				Some(c) => c,
1747				None => {
1748					gum::warn!(
1749						target: LOG_TARGET,
1750						?block_hash,
1751						?candidate_hash,
1752						"Missing candidate entry",
1753					);
1754
1755					continue;
1756				},
1757			};
1758
1759			match candidate_entry.approval_entry(&block_hash) {
1760				Some(approval_entry) => {
1761					match approval_entry.local_statements() {
1762						(None, None) => {
1763							if approval_entry
1764								.our_assignment()
1765								.map(|assignment| !assignment.triggered())
1766								.unwrap_or(false)
1767							{
1768								actions.push(Action::ScheduleWakeup {
1769									block_hash,
1770									block_number: block_entry.block_number(),
1771									candidate_hash: *candidate_hash,
1772									tick: state.clock.tick_now() + RESTART_WAKEUP_DELAY,
1773								})
1774							}
1775						},
1776						(None, Some(_)) => {}, // second is impossible case.
1777						(Some(assignment), None) => {
1778							let claimed_core_indices =
1779								get_assignment_core_indices(&assignment.cert().kind);
1780
1781							if block_entry.has_candidates_pending_signature() {
1782								delayed_approvals_timers.maybe_arm_timer(
1783									state.clock.tick_now(),
1784									state.clock.as_ref(),
1785									block_entry.block_hash(),
1786									assignment.validator_index(),
1787								)
1788							}
1789
1790							match cores_to_candidate_indices(&claimed_core_indices, &block_entry) {
1791								Ok(bitfield) => {
1792									gum::debug!(
1793										target: LOG_TARGET,
1794										candidate_hash = ?candidate_entry.candidate_receipt().hash(),
1795										?block_hash,
1796										"Discovered, triggered assignment, not approved yet",
1797									);
1798
1799									let indirect_cert = IndirectAssignmentCertV2 {
1800										block_hash,
1801										validator: assignment.validator_index(),
1802										cert: assignment.cert().clone(),
1803									};
1804									messages.push(
1805										ApprovalDistributionMessage::DistributeAssignment(
1806											indirect_cert.clone(),
1807											bitfield.clone(),
1808										),
1809									);
1810
1811									if !block_entry.candidate_is_pending_signature(*candidate_hash)
1812									{
1813										actions.push(Action::LaunchApproval {
1814											claimed_candidate_indices: bitfield,
1815											candidate_hash: candidate_entry
1816												.candidate_receipt()
1817												.hash(),
1818											indirect_cert,
1819											assignment_tranche: assignment.tranche(),
1820											relay_block_hash: block_hash,
1821											session: block_entry.session(),
1822											candidate: candidate_entry.candidate_receipt().clone(),
1823											backing_group: approval_entry.backing_group(),
1824											distribute_assignment: false,
1825											core_index: Some(*core_index),
1826										});
1827									}
1828								},
1829								Err(err) => {
1830									// Should never happen. If we fail here it means the
1831									// assignment is null (no cores claimed).
1832									gum::warn!(
1833										target: LOG_TARGET,
1834										?block_hash,
1835										?candidate_hash,
1836										?err,
1837										"Failed to create assignment bitfield",
1838									);
1839								},
1840							}
1841						},
1842						(Some(assignment), Some(approval_sig)) => {
1843							let claimed_core_indices =
1844								get_assignment_core_indices(&assignment.cert().kind);
1845							match cores_to_candidate_indices(&claimed_core_indices, &block_entry) {
1846								Ok(bitfield) => messages.push(
1847									ApprovalDistributionMessage::DistributeAssignment(
1848										IndirectAssignmentCertV2 {
1849											block_hash,
1850											validator: assignment.validator_index(),
1851											cert: assignment.cert().clone(),
1852										},
1853										bitfield,
1854									),
1855								),
1856								Err(err) => {
1857									gum::warn!(
1858										target: LOG_TARGET,
1859										?block_hash,
1860										?candidate_hash,
1861										?err,
1862										"Failed to create assignment bitfield",
1863									);
1864									// If we didn't send assignment, we don't send approval.
1865									continue;
1866								},
1867							}
1868							if signatures_queued
1869								.insert(approval_sig.signed_candidates_indices.clone())
1870							{
1871								approvals.push(ApprovalDistributionMessage::DistributeApproval(
1872									IndirectSignedApprovalVoteV2 {
1873										block_hash,
1874										candidate_indices: approval_sig.signed_candidates_indices,
1875										validator: assignment.validator_index(),
1876										signature: approval_sig.signature,
1877									},
1878								))
1879							};
1880						},
1881					}
1882				},
1883				None => {
1884					gum::warn!(
1885						target: LOG_TARGET,
1886						?block_hash,
1887						?candidate_hash,
1888						"Missing approval entry",
1889					);
1890				},
1891			}
1892		}
1893	}
1894
1895	messages[0] = ApprovalDistributionMessage::NewBlocks(approval_meta);
1896	// Approvals are appended at the end, to make sure all assignments are sent
1897	// before the approvals, otherwise if they arrive ahead in approval-distribution
1898	// they will be ignored.
1899	messages.extend(approvals.into_iter());
1900	Ok((messages, actions))
1901}
1902
1903// Handle an incoming signal from the overseer. Returns true if execution should conclude.
1904async fn handle_from_overseer<
1905	Sender: SubsystemSender<ChainApiMessage>
1906		+ SubsystemSender<RuntimeApiMessage>
1907		+ SubsystemSender<ChainSelectionMessage>
1908		+ Clone,
1909	ADSender: SubsystemSender<ApprovalDistributionMessage>,
1910>(
1911	sender: &mut Sender,
1912	approval_voting_sender: &mut ADSender,
1913	spawn_handle: &Arc<dyn overseer::gen::Spawner + 'static>,
1914	state: &mut State,
1915	db: &mut OverlayedBackend<'_, impl Backend>,
1916	session_info_provider: &mut RuntimeInfo,
1917	metrics: &Metrics,
1918	x: FromOrchestra<ApprovalVotingMessage>,
1919	last_finalized_height: &mut Option<BlockNumber>,
1920	wakeups: &mut Wakeups,
1921) -> SubsystemResult<Vec<Action>> {
1922	let actions = match x {
1923		FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update)) => {
1924			let mut actions = Vec::new();
1925			if let Some(activated) = update.activated {
1926				let head = activated.hash;
1927				match import::handle_new_head(
1928					sender,
1929					approval_voting_sender,
1930					state,
1931					db,
1932					session_info_provider,
1933					head,
1934					last_finalized_height,
1935				)
1936				.await
1937				{
1938					Err(e) => return Err(SubsystemError::with_origin("db", e)),
1939					Ok(block_imported_candidates) => {
1940						// Schedule wakeups for all imported candidates.
1941						for block_batch in block_imported_candidates {
1942							gum::debug!(
1943								target: LOG_TARGET,
1944								block_number = ?block_batch.block_number,
1945								block_hash = ?block_batch.block_hash,
1946								num_candidates = block_batch.imported_candidates.len(),
1947								"Imported new block.",
1948							);
1949
1950							state.no_show_stats.maybe_print(block_batch.block_number);
1951
1952							for (c_hash, c_entry) in block_batch.imported_candidates {
1953								metrics.on_candidate_imported();
1954
1955								let our_tranche = c_entry
1956									.approval_entry(&block_batch.block_hash)
1957									.and_then(|a| a.our_assignment().map(|a| a.tranche()));
1958
1959								if let Some(our_tranche) = our_tranche {
1960									let tick = our_tranche as Tick + block_batch.block_tick;
1961									gum::trace!(
1962										target: LOG_TARGET,
1963										tranche = our_tranche,
1964										candidate_hash = ?c_hash,
1965										block_hash = ?block_batch.block_hash,
1966										block_tick = block_batch.block_tick,
1967										"Scheduling first wakeup.",
1968									);
1969
1970									// Our first wakeup will just be the tranche of our assignment,
1971									// if any. This will likely be superseded by incoming
1972									// assignments and approvals which trigger rescheduling.
1973									actions.push(Action::ScheduleWakeup {
1974										block_hash: block_batch.block_hash,
1975										block_number: block_batch.block_number,
1976										candidate_hash: c_hash,
1977										tick,
1978									});
1979								}
1980							}
1981						}
1982					},
1983				}
1984			}
1985
1986			actions
1987		},
1988		FromOrchestra::Signal(OverseerSignal::BlockFinalized(block_hash, block_number)) => {
1989			gum::debug!(target: LOG_TARGET, ?block_hash, ?block_number, "Block finalized");
1990			*last_finalized_height = Some(block_number);
1991
1992			crate::ops::canonicalize(db, block_number, block_hash)
1993				.map_err(|e| SubsystemError::with_origin("db", e))?;
1994
1995			// `prune_finalized_wakeups` prunes all finalized block hashes. We prune spans
1996			// accordingly.
1997			wakeups.prune_finalized_wakeups(block_number);
1998			state.cleanup_assignments_gathering_timestamp(block_number);
1999
2000			// // `prune_finalized_wakeups` prunes all finalized block hashes. We prune spans
2001			// accordingly. let hash_set =
2002			// wakeups.block_numbers.values().flatten().collect::<HashSet<_>>(); state.spans.
2003			// retain(|hash, _| hash_set.contains(hash));
2004
2005			Vec::new()
2006		},
2007		FromOrchestra::Signal(OverseerSignal::Conclude) => {
2008			vec![Action::Conclude]
2009		},
2010		FromOrchestra::Communication { msg } => match msg {
2011			ApprovalVotingMessage::ImportAssignment(checked_assignment, tx) => {
2012				let (check_outcome, actions) =
2013					import_assignment(sender, state, db, session_info_provider, checked_assignment)
2014						.await?;
2015				// approval-distribution makes sure this assignment is valid and expected,
2016				// so this import should never fail, if it does it might mean one of two things,
2017				// there is a bug in the code or the two subsystems got out of sync.
2018				if let AssignmentCheckResult::Bad(ref err) = check_outcome {
2019					gum::debug!(target: LOG_TARGET, ?err, "Unexpected fail when importing an assignment");
2020				}
2021				let _ = tx.map(|tx| tx.send(check_outcome));
2022				actions
2023			},
2024			ApprovalVotingMessage::ImportApproval(a, tx) => {
2025				let result =
2026					import_approval(sender, state, db, session_info_provider, metrics, a, &wakeups)
2027						.await?;
2028				// approval-distribution makes sure this vote is valid and expected,
2029				// so this import should never fail, if it does it might mean one of two things,
2030				// there is a bug in the code or the two subsystems got out of sync.
2031				if let ApprovalCheckResult::Bad(ref err) = result.1 {
2032					gum::debug!(target: LOG_TARGET, ?err, "Unexpected fail when importing an approval");
2033				}
2034				let _ = tx.map(|tx| tx.send(result.1));
2035
2036				result.0
2037			},
2038			ApprovalVotingMessage::ApprovedAncestor(target, lower_bound, res) => {
2039				match handle_approved_ancestor(sender, db, target, lower_bound, wakeups, &metrics)
2040					.await
2041				{
2042					Ok(v) => {
2043						let _ = res.send(v);
2044					},
2045					Err(e) => {
2046						let _ = res.send(None);
2047						return Err(e);
2048					},
2049				}
2050
2051				Vec::new()
2052			},
2053			ApprovalVotingMessage::GetApprovalSignaturesForCandidate(candidate_hash, tx) => {
2054				metrics.on_candidate_signatures_request();
2055				get_approval_signatures_for_candidate(
2056					approval_voting_sender.clone(),
2057					spawn_handle,
2058					db,
2059					candidate_hash,
2060					tx,
2061				)
2062				.await?;
2063				Vec::new()
2064			},
2065		},
2066	};
2067
2068	Ok(actions)
2069}
2070
2071/// Retrieve approval signatures.
2072///
2073/// This involves an unbounded message send to approval-distribution, the caller has to ensure that
2074/// calls to this function are infrequent and bounded.
2075#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)]
2076async fn get_approval_signatures_for_candidate<
2077	Sender: SubsystemSender<ApprovalDistributionMessage>,
2078>(
2079	mut sender: Sender,
2080	spawn_handle: &Arc<dyn overseer::gen::Spawner + 'static>,
2081	db: &OverlayedBackend<'_, impl Backend>,
2082	candidate_hash: CandidateHash,
2083	tx: oneshot::Sender<
2084		HashMap<ValidatorIndex, (CoalescedApprovalCandidateHashes, ValidatorSignature)>,
2085	>,
2086) -> SubsystemResult<()> {
2087	let send_votes = |votes| {
2088		if let Err(_) = tx.send(votes) {
2089			gum::debug!(
2090				target: LOG_TARGET,
2091				"Sending approval signatures back failed, as receiver got closed."
2092			);
2093		}
2094	};
2095	let entry = match db.load_candidate_entry(&candidate_hash)? {
2096		None => {
2097			send_votes(HashMap::new());
2098			gum::debug!(
2099				target: LOG_TARGET,
2100				?candidate_hash,
2101				"Sent back empty votes because the candidate was not found in db."
2102			);
2103			return Ok(());
2104		},
2105		Some(e) => e,
2106	};
2107
2108	let relay_hashes = entry.block_assignments.keys();
2109
2110	let mut candidate_indices = HashSet::new();
2111	let mut candidate_indices_to_candidate_hashes: HashMap<
2112		Hash,
2113		HashMap<CandidateIndex, CandidateHash>,
2114	> = HashMap::new();
2115
2116	// Retrieve `CoreIndices`/`CandidateIndices` as required by approval-distribution:
2117	for hash in relay_hashes {
2118		let entry = match db.load_block_entry(hash)? {
2119			None => {
2120				gum::debug!(
2121					target: LOG_TARGET,
2122					?candidate_hash,
2123					?hash,
2124					"Block entry for assignment missing."
2125				);
2126				continue;
2127			},
2128			Some(e) => e,
2129		};
2130		for (candidate_index, (_core_index, c_hash)) in entry.candidates().iter().enumerate() {
2131			if c_hash == &candidate_hash {
2132				candidate_indices.insert((*hash, candidate_index as u32));
2133			}
2134			candidate_indices_to_candidate_hashes
2135				.entry(*hash)
2136				.or_default()
2137				.insert(candidate_index as _, *c_hash);
2138		}
2139	}
2140
2141	let get_approvals = async move {
2142		let (tx_distribution, rx_distribution) = oneshot::channel();
2143		sender.send_unbounded_message(ApprovalDistributionMessage::GetApprovalSignatures(
2144			candidate_indices,
2145			tx_distribution,
2146		));
2147
2148		// Because of the unbounded sending and the nature of the call (just fetching data from
2149		// state), this should not block long:
2150		match rx_distribution.timeout(WAIT_FOR_SIGS_TIMEOUT).await {
2151			None => {
2152				gum::warn!(
2153					target: LOG_TARGET,
2154					"Waiting for approval signatures timed out - dead lock?"
2155				);
2156			},
2157			Some(Err(_)) => gum::debug!(
2158				target: LOG_TARGET,
2159				"Request for approval signatures got cancelled by `approval-distribution`."
2160			),
2161			Some(Ok(votes)) => {
2162				let votes = votes
2163					.into_iter()
2164					.filter_map(|(validator_index, (hash, signed_candidates_indices, signature))| {
2165						let candidates_hashes = candidate_indices_to_candidate_hashes.get(&hash);
2166
2167						if candidates_hashes.is_none() {
2168							gum::warn!(
2169								target: LOG_TARGET,
2170								?hash,
2171								"Possible bug! Could not find map of candidate_hashes for block hash received from approval-distribution"
2172							);
2173						}
2174
2175						let num_signed_candidates = signed_candidates_indices.len();
2176
2177						let signed_candidates_hashes: Vec<CandidateHash> =
2178							signed_candidates_indices
2179								.into_iter()
2180								.filter_map(|candidate_index| {
2181									candidates_hashes.and_then(|candidate_hashes| {
2182										if let Some(candidate_hash) =
2183											candidate_hashes.get(&candidate_index)
2184										{
2185											Some(*candidate_hash)
2186										} else {
2187											gum::warn!(
2188												target: LOG_TARGET,
2189												?candidate_index,
2190												"Possible bug! Could not find candidate hash for candidate_index coming from approval-distribution"
2191											);
2192											None
2193										}
2194									})
2195								})
2196								.collect();
2197						if num_signed_candidates == signed_candidates_hashes.len() {
2198							match signed_candidates_hashes.try_into() {
2199								Ok(signed_candidates_hashes) =>
2200									Some((validator_index, (signed_candidates_hashes, signature))),
2201								Err(_) => {
2202									gum::warn!(
2203										target: LOG_TARGET,
2204										"Skipping approval signature coalescing more than MAX_COALESCE_APPROVALS candidates"
2205									);
2206									None
2207								},
2208							}
2209						} else {
2210							gum::warn!(
2211								target: LOG_TARGET,
2212								"Possible bug! Could not find all hashes for candidates coming from approval-distribution"
2213							);
2214							None
2215						}
2216					})
2217					.collect();
2218				send_votes(votes)
2219			},
2220		}
2221	};
2222
2223	// No need to block subsystem on this (also required to break cycle).
2224	// We should not be sending this message frequently - caller must make sure this is bounded.
2225	gum::trace!(
2226		target: LOG_TARGET,
2227		?candidate_hash,
2228		"Spawning task for fetching signatures from approval-distribution"
2229	);
2230	spawn_handle.spawn(
2231		"get-approval-signatures",
2232		Some("approval-voting-subsystem"),
2233		Box::pin(get_approvals),
2234	);
2235	Ok(())
2236}
2237
2238#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)]
2239async fn handle_approved_ancestor<Sender: SubsystemSender<ChainApiMessage>>(
2240	sender: &mut Sender,
2241	db: &OverlayedBackend<'_, impl Backend>,
2242	target: Hash,
2243	lower_bound: BlockNumber,
2244	wakeups: &Wakeups,
2245	metrics: &Metrics,
2246) -> SubsystemResult<Option<HighestApprovedAncestorBlock>> {
2247	const MAX_TRACING_WINDOW: usize = 200;
2248	const ABNORMAL_DEPTH_THRESHOLD: usize = 5;
2249	const LOGGING_DEPTH_THRESHOLD: usize = 10;
2250
2251	let mut all_approved_max = None;
2252
2253	let target_number = {
2254		let (tx, rx) = oneshot::channel();
2255
2256		sender.send_message(ChainApiMessage::BlockNumber(target, tx)).await;
2257
2258		match rx.await {
2259			Ok(Ok(Some(n))) => n,
2260			Ok(Ok(None)) => return Ok(None),
2261			Ok(Err(_)) | Err(_) => return Ok(None),
2262		}
2263	};
2264
2265	if target_number <= lower_bound {
2266		return Ok(None);
2267	}
2268
2269	// request ancestors up to but not including the lower bound,
2270	// as a vote on the lower bound is implied if we cannot find
2271	// anything else.
2272	let ancestry = if target_number > lower_bound + 1 {
2273		let (tx, rx) = oneshot::channel();
2274
2275		sender
2276			.send_message(ChainApiMessage::Ancestors {
2277				hash: target,
2278				k: (target_number - (lower_bound + 1)) as usize,
2279				response_channel: tx,
2280			})
2281			.await;
2282
2283		match rx.await {
2284			Ok(Ok(a)) => a,
2285			Err(_) | Ok(Err(_)) => return Ok(None),
2286		}
2287	} else {
2288		Vec::new()
2289	};
2290	let ancestry_len = ancestry.len();
2291
2292	let mut block_descriptions = Vec::new();
2293
2294	let mut bits: BitVec<u8, Lsb0> = Default::default();
2295	for (i, block_hash) in std::iter::once(target).chain(ancestry).enumerate() {
2296		// Block entries should be present as the assumption is that
2297		// nothing here is finalized. If we encounter any missing block
2298		// entries we can fail.
2299		let entry = match db.load_block_entry(&block_hash)? {
2300			None => {
2301				let block_number = target_number.saturating_sub(i as u32);
2302				gum::info!(
2303					target: LOG_TARGET,
2304					unknown_number = ?block_number,
2305					unknown_hash = ?block_hash,
2306					"Chain between ({}, {}) and {} not fully known. Forcing vote on {}",
2307					target,
2308					target_number,
2309					lower_bound,
2310					lower_bound,
2311				);
2312				return Ok(None);
2313			},
2314			Some(b) => b,
2315		};
2316
2317		// even if traversing millions of blocks this is fairly cheap and always dwarfed by the
2318		// disk lookups.
2319		bits.push(entry.is_fully_approved());
2320		if entry.is_fully_approved() {
2321			if all_approved_max.is_none() {
2322				// First iteration of the loop is target, i = 0. After that,
2323				// ancestry is moving backwards.
2324				all_approved_max = Some((block_hash, target_number - i as BlockNumber));
2325			}
2326			block_descriptions.push(BlockDescription {
2327				block_hash,
2328				session: entry.session(),
2329				candidates: entry
2330					.candidates()
2331					.iter()
2332					.map(|(_idx, candidate_hash)| *candidate_hash)
2333					.collect(),
2334			});
2335		} else if bits.len() <= ABNORMAL_DEPTH_THRESHOLD {
2336			all_approved_max = None;
2337			block_descriptions.clear();
2338		} else {
2339			all_approved_max = None;
2340			block_descriptions.clear();
2341
2342			let unapproved: Vec<_> = entry.unapproved_candidates().collect();
2343			gum::debug!(
2344				target: LOG_TARGET,
2345				"Block {} is {} blocks deep and has {}/{} candidates unapproved",
2346				block_hash,
2347				bits.len() - 1,
2348				unapproved.len(),
2349				entry.candidates().len(),
2350			);
2351			if ancestry_len >= LOGGING_DEPTH_THRESHOLD && i > ancestry_len - LOGGING_DEPTH_THRESHOLD
2352			{
2353				gum::trace!(
2354					target: LOG_TARGET,
2355					?block_hash,
2356					"Unapproved candidates at depth {}: {:?}",
2357					bits.len(),
2358					unapproved
2359				)
2360			}
2361			metrics.on_unapproved_candidates_in_unfinalized_chain(unapproved.len());
2362			for candidate_hash in unapproved {
2363				match db.load_candidate_entry(&candidate_hash)? {
2364					None => {
2365						gum::warn!(
2366							target: LOG_TARGET,
2367							?candidate_hash,
2368							"Missing expected candidate in DB",
2369						);
2370
2371						continue;
2372					},
2373					Some(c_entry) => match c_entry.approval_entry(&block_hash) {
2374						None => {
2375							gum::warn!(
2376								target: LOG_TARGET,
2377								?candidate_hash,
2378								?block_hash,
2379								"Missing expected approval entry under candidate.",
2380							);
2381						},
2382						Some(a_entry) => {
2383							let status = || {
2384								let n_assignments = a_entry.n_assignments();
2385
2386								// Take the approvals, filtered by the assignments
2387								// for this block.
2388								let n_approvals = c_entry
2389									.approvals()
2390									.iter()
2391									.by_vals()
2392									.enumerate()
2393									.filter(|(i, approved)| {
2394										*approved && a_entry.is_assigned(ValidatorIndex(*i as _))
2395									})
2396									.count();
2397
2398								format!(
2399									"{}/{}/{}",
2400									n_assignments,
2401									n_approvals,
2402									a_entry.n_validators(),
2403								)
2404							};
2405
2406							match a_entry.our_assignment() {
2407								None => gum::debug!(
2408									target: LOG_TARGET,
2409									?candidate_hash,
2410									?block_hash,
2411									status = %status(),
2412									"no assignment."
2413								),
2414								Some(a) => {
2415									let tranche = a.tranche();
2416									let triggered = a.triggered();
2417
2418									let next_wakeup =
2419										wakeups.wakeup_for(block_hash, candidate_hash);
2420
2421									let approved =
2422										triggered && { a_entry.local_statements().1.is_some() };
2423
2424									gum::debug!(
2425										target: LOG_TARGET,
2426										?candidate_hash,
2427										?block_hash,
2428										tranche,
2429										?next_wakeup,
2430										status = %status(),
2431										triggered,
2432										approved,
2433										"assigned."
2434									);
2435								},
2436							}
2437						},
2438					},
2439				}
2440			}
2441		}
2442	}
2443
2444	gum::debug!(
2445		target: LOG_TARGET,
2446		"approved blocks {}-[{}]-{}",
2447		target_number,
2448		{
2449			// formatting to divide bits by groups of 10.
2450			// when comparing logs on multiple machines where the exact vote
2451			// targets may differ, this grouping is useful.
2452			let mut s = String::with_capacity(bits.len());
2453			for (i, bit) in bits.iter().enumerate().take(MAX_TRACING_WINDOW) {
2454				s.push(if *bit { '1' } else { '0' });
2455				if (target_number - i as u32).is_multiple_of(10) && i != bits.len() - 1 {
2456					s.push(' ');
2457				}
2458			}
2459
2460			s
2461		},
2462		if bits.len() > MAX_TRACING_WINDOW {
2463			format!(
2464				"{}... (truncated due to large window)",
2465				target_number - MAX_TRACING_WINDOW as u32 + 1,
2466			)
2467		} else {
2468			format!("{}", lower_bound + 1)
2469		},
2470	);
2471
2472	// `reverse()` to obtain the ascending order from lowest to highest
2473	// block within the candidates, which is the expected order
2474	block_descriptions.reverse();
2475
2476	let all_approved_max =
2477		all_approved_max.map(|(hash, block_number)| HighestApprovedAncestorBlock {
2478			hash,
2479			number: block_number,
2480			descriptions: block_descriptions,
2481		});
2482
2483	Ok(all_approved_max)
2484}
2485
2486// `Option::cmp` treats `None` as less than `Some`.
2487fn min_prefer_some<T: std::cmp::Ord>(a: Option<T>, b: Option<T>) -> Option<T> {
2488	match (a, b) {
2489		(None, None) => None,
2490		(None, Some(x)) | (Some(x), None) => Some(x),
2491		(Some(x), Some(y)) => Some(std::cmp::min(x, y)),
2492	}
2493}
2494
2495fn schedule_wakeup_action(
2496	approval_entry: &ApprovalEntry,
2497	block_hash: Hash,
2498	block_number: BlockNumber,
2499	candidate_hash: CandidateHash,
2500	block_tick: Tick,
2501	tick_now: Tick,
2502	required_tranches: RequiredTranches,
2503) -> Option<Action> {
2504	let maybe_action = match required_tranches {
2505		_ if approval_entry.is_approved() => None,
2506		RequiredTranches::All => None,
2507		RequiredTranches::Exact { next_no_show, last_assignment_tick, .. } => {
2508			// Take the earlier of the next no show or the last assignment tick + required delay,
2509			// only considering the latter if it is after the current moment.
2510			min_prefer_some(
2511				last_assignment_tick.map(|l| l + APPROVAL_DELAY).filter(|t| t > &tick_now),
2512				next_no_show,
2513			)
2514			.map(|tick| Action::ScheduleWakeup {
2515				block_hash,
2516				block_number,
2517				candidate_hash,
2518				tick,
2519			})
2520		},
2521		RequiredTranches::Pending { considered, next_no_show, clock_drift, .. } => {
2522			// select the minimum of `next_no_show`, or the tick of the next non-empty tranche
2523			// after `considered`, including any tranche that might contain our own untriggered
2524			// assignment.
2525			let next_non_empty_tranche = {
2526				let next_announced = approval_entry
2527					.tranches()
2528					.iter()
2529					.skip_while(|t| t.tranche() <= considered)
2530					.map(|t| t.tranche())
2531					.next();
2532
2533				let our_untriggered = approval_entry.our_assignment().and_then(|t| {
2534					if !t.triggered() && t.tranche() > considered {
2535						Some(t.tranche())
2536					} else {
2537						None
2538					}
2539				});
2540
2541				// Apply the clock drift to these tranches.
2542				min_prefer_some(next_announced, our_untriggered)
2543					.map(|t| t as Tick + block_tick + clock_drift)
2544			};
2545
2546			min_prefer_some(next_non_empty_tranche, next_no_show).map(|tick| {
2547				Action::ScheduleWakeup { block_hash, block_number, candidate_hash, tick }
2548			})
2549		},
2550	};
2551
2552	match maybe_action {
2553		Some(Action::ScheduleWakeup { ref tick, .. }) => gum::trace!(
2554			target: LOG_TARGET,
2555			tick,
2556			?candidate_hash,
2557			?block_hash,
2558			block_tick,
2559			"Scheduling next wakeup.",
2560		),
2561		None => gum::trace!(
2562			target: LOG_TARGET,
2563			?candidate_hash,
2564			?block_hash,
2565			block_tick,
2566			"No wakeup needed.",
2567		),
2568		Some(_) => {}, // unreachable
2569	}
2570
2571	maybe_action
2572}
2573
2574async fn import_assignment<Sender>(
2575	sender: &mut Sender,
2576	state: &State,
2577	db: &mut OverlayedBackend<'_, impl Backend>,
2578	session_info_provider: &mut RuntimeInfo,
2579	checked_assignment: CheckedIndirectAssignment,
2580) -> SubsystemResult<(AssignmentCheckResult, Vec<Action>)>
2581where
2582	Sender: SubsystemSender<RuntimeApiMessage>,
2583{
2584	let tick_now = state.clock.tick_now();
2585	let assignment = checked_assignment.assignment();
2586	let candidate_indices = checked_assignment.candidate_indices();
2587	let tranche = checked_assignment.tranche();
2588
2589	let block_entry = match db.load_block_entry(&assignment.block_hash)? {
2590		Some(b) => b,
2591		None => {
2592			return Ok((
2593				AssignmentCheckResult::Bad(AssignmentCheckError::UnknownBlock(
2594					assignment.block_hash,
2595				)),
2596				Vec::new(),
2597			))
2598		},
2599	};
2600
2601	let session_info = match get_session_info_by_index(
2602		session_info_provider,
2603		sender,
2604		block_entry.parent_hash(),
2605		block_entry.session(),
2606	)
2607	.await
2608	{
2609		Some(s) => s,
2610		None => {
2611			return Ok((
2612				AssignmentCheckResult::Bad(AssignmentCheckError::UnknownSessionIndex(
2613					block_entry.session(),
2614				)),
2615				Vec::new(),
2616			))
2617		},
2618	};
2619
2620	let n_cores = session_info.n_cores as usize;
2621
2622	// Early check the candidate bitfield and core bitfields lengths < `n_cores`.
2623	// Core bitfield length is checked later in `check_assignment_cert`.
2624	if candidate_indices.len() > n_cores {
2625		gum::debug!(
2626			target: LOG_TARGET,
2627			validator = assignment.validator.0,
2628			n_cores,
2629			candidate_bitfield_len = ?candidate_indices.len(),
2630			"Oversized bitfield",
2631		);
2632
2633		return Ok((
2634			AssignmentCheckResult::Bad(AssignmentCheckError::InvalidBitfield(
2635				candidate_indices.len(),
2636			)),
2637			Vec::new(),
2638		));
2639	}
2640
2641	let mut claimed_core_indices = Vec::new();
2642	let mut assigned_candidate_hashes = Vec::new();
2643
2644	for candidate_index in candidate_indices.iter_ones() {
2645		let (claimed_core_index, assigned_candidate_hash) =
2646			match block_entry.candidate(candidate_index) {
2647				Some((c, h)) => (*c, *h),
2648				None => {
2649					return Ok((
2650						AssignmentCheckResult::Bad(AssignmentCheckError::InvalidCandidateIndex(
2651							candidate_index as _,
2652						)),
2653						Vec::new(),
2654					))
2655				}, // no candidate at core.
2656			};
2657
2658		let mut candidate_entry = match db.load_candidate_entry(&assigned_candidate_hash)? {
2659			Some(c) => c,
2660			None => {
2661				return Ok((
2662					AssignmentCheckResult::Bad(AssignmentCheckError::InvalidCandidate(
2663						candidate_index as _,
2664						assigned_candidate_hash,
2665					)),
2666					Vec::new(),
2667				))
2668			}, // no candidate at core.
2669		};
2670
2671		if candidate_entry.approval_entry_mut(&assignment.block_hash).is_none() {
2672			return Ok((
2673				AssignmentCheckResult::Bad(AssignmentCheckError::Internal(
2674					assignment.block_hash,
2675					assigned_candidate_hash,
2676				)),
2677				Vec::new(),
2678			));
2679		};
2680
2681		claimed_core_indices.push(claimed_core_index);
2682		assigned_candidate_hashes.push(assigned_candidate_hash);
2683	}
2684
2685	// Error on null assignments.
2686	if claimed_core_indices.is_empty() {
2687		return Ok((
2688			AssignmentCheckResult::Bad(AssignmentCheckError::InvalidCert(
2689				assignment.validator,
2690				format!("{:?}", InvalidAssignmentReason::NullAssignment),
2691			)),
2692			Vec::new(),
2693		));
2694	}
2695
2696	let mut actions = Vec::new();
2697	let res = {
2698		let mut is_duplicate = true;
2699		// Import the assignments for all cores in the cert.
2700		for (assigned_candidate_hash, candidate_index) in
2701			assigned_candidate_hashes.iter().zip(candidate_indices.iter_ones())
2702		{
2703			let mut candidate_entry = match db.load_candidate_entry(&assigned_candidate_hash)? {
2704				Some(c) => c,
2705				None => {
2706					return Ok((
2707						AssignmentCheckResult::Bad(AssignmentCheckError::InvalidCandidate(
2708							candidate_index as _,
2709							*assigned_candidate_hash,
2710						)),
2711						Vec::new(),
2712					))
2713				},
2714			};
2715
2716			let approval_entry = match candidate_entry.approval_entry_mut(&assignment.block_hash) {
2717				Some(a) => a,
2718				None => {
2719					return Ok((
2720						AssignmentCheckResult::Bad(AssignmentCheckError::Internal(
2721							assignment.block_hash,
2722							*assigned_candidate_hash,
2723						)),
2724						Vec::new(),
2725					))
2726				},
2727			};
2728
2729			let is_duplicate_for_candidate = approval_entry.is_assigned(assignment.validator);
2730			is_duplicate &= is_duplicate_for_candidate;
2731			approval_entry.import_assignment(
2732				tranche,
2733				assignment.validator,
2734				tick_now,
2735				is_duplicate_for_candidate,
2736			);
2737
2738			// We've imported a new assignment, so we need to schedule a wake-up for when that might
2739			// no-show.
2740			if let Some((approval_entry, status)) = state
2741				.approval_status(sender, session_info_provider, &block_entry, &candidate_entry)
2742				.await
2743			{
2744				actions.extend(schedule_wakeup_action(
2745					approval_entry,
2746					block_entry.block_hash(),
2747					block_entry.block_number(),
2748					*assigned_candidate_hash,
2749					status.block_tick,
2750					tick_now,
2751					status.required_tranches,
2752				));
2753			}
2754
2755			// We also write the candidate entry as it now contains the new candidate.
2756			db.write_candidate_entry(candidate_entry.into());
2757		}
2758
2759		// Since we don't account for tranche in distribution message fingerprinting, some
2760		// validators can be assigned to the same core (VRF modulo vs VRF delay). These can be
2761		// safely ignored. However, if an assignment is for multiple cores (these are only
2762		// tranche0), we cannot ignore it, because it would mean ignoring other non duplicate
2763		// assignments.
2764		if is_duplicate {
2765			AssignmentCheckResult::AcceptedDuplicate
2766		} else if candidate_indices.count_ones() > 1 {
2767			gum::trace!(
2768				target: LOG_TARGET,
2769				validator = assignment.validator.0,
2770				candidate_hashes = ?assigned_candidate_hashes,
2771				assigned_cores = ?claimed_core_indices,
2772				?tranche,
2773				"Imported assignments for multiple cores.",
2774			);
2775
2776			AssignmentCheckResult::Accepted
2777		} else {
2778			gum::trace!(
2779				target: LOG_TARGET,
2780				validator = assignment.validator.0,
2781				candidate_hashes = ?assigned_candidate_hashes,
2782				assigned_cores = ?claimed_core_indices,
2783				"Imported assignment for a single core.",
2784			);
2785
2786			AssignmentCheckResult::Accepted
2787		}
2788	};
2789
2790	Ok((res, actions))
2791}
2792
2793async fn import_approval<Sender>(
2794	sender: &mut Sender,
2795	state: &mut State,
2796	db: &mut OverlayedBackend<'_, impl Backend>,
2797	session_info_provider: &mut RuntimeInfo,
2798	metrics: &Metrics,
2799	approval: CheckedIndirectSignedApprovalVote,
2800	wakeups: &Wakeups,
2801) -> SubsystemResult<(Vec<Action>, ApprovalCheckResult)>
2802where
2803	Sender: SubsystemSender<RuntimeApiMessage>,
2804{
2805	macro_rules! respond_early {
2806		($e: expr) => {{
2807			return Ok((Vec::new(), $e));
2808		}};
2809	}
2810
2811	let block_entry = match db.load_block_entry(&approval.block_hash)? {
2812		Some(b) => b,
2813		None => {
2814			respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::UnknownBlock(
2815				approval.block_hash
2816			),))
2817		},
2818	};
2819
2820	let approved_candidates_info: Result<Vec<(CandidateIndex, CandidateHash)>, ApprovalCheckError> =
2821		approval
2822			.candidate_indices
2823			.iter_ones()
2824			.map(|candidate_index| {
2825				block_entry
2826					.candidate(candidate_index)
2827					.ok_or(ApprovalCheckError::InvalidCandidateIndex(candidate_index as _))
2828					.map(|candidate| (candidate_index as _, candidate.1))
2829			})
2830			.collect();
2831
2832	let approved_candidates_info = match approved_candidates_info {
2833		Ok(approved_candidates_info) => approved_candidates_info,
2834		Err(err) => {
2835			respond_early!(ApprovalCheckResult::Bad(err))
2836		},
2837	};
2838
2839	gum::trace!(
2840		target: LOG_TARGET,
2841		"Received approval for num_candidates {:}",
2842		approval.candidate_indices.count_ones()
2843	);
2844
2845	let mut actions = Vec::new();
2846	for (approval_candidate_index, approved_candidate_hash) in approved_candidates_info {
2847		let block_entry = match db.load_block_entry(&approval.block_hash)? {
2848			Some(b) => b,
2849			None => {
2850				respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::UnknownBlock(
2851					approval.block_hash
2852				),))
2853			},
2854		};
2855
2856		let candidate_entry = match db.load_candidate_entry(&approved_candidate_hash)? {
2857			Some(c) => c,
2858			None => {
2859				respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::InvalidCandidate(
2860					approval_candidate_index,
2861					approved_candidate_hash
2862				),))
2863			},
2864		};
2865
2866		// Don't accept approvals until assignment.
2867		match candidate_entry.approval_entry(&approval.block_hash) {
2868			None => {
2869				respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::Internal(
2870					approval.block_hash,
2871					approved_candidate_hash
2872				),))
2873			},
2874			Some(e) if !e.is_assigned(approval.validator) => {
2875				respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::NoAssignment(
2876					approval.validator
2877				),))
2878			},
2879			_ => {},
2880		}
2881
2882		gum::trace!(
2883			target: LOG_TARGET,
2884			validator_index = approval.validator.0,
2885			candidate_hash = ?approved_candidate_hash,
2886			para_id = ?candidate_entry.candidate_receipt().descriptor.para_id(),
2887			"Importing approval vote",
2888		);
2889
2890		let new_actions = advance_approval_state(
2891			sender,
2892			state,
2893			db,
2894			session_info_provider,
2895			&metrics,
2896			block_entry,
2897			approved_candidate_hash,
2898			candidate_entry,
2899			ApprovalStateTransition::RemoteApproval(approval.validator),
2900			wakeups,
2901		)
2902		.await;
2903		actions.extend(new_actions);
2904	}
2905
2906	// importing the approval can be heavy as it may trigger acceptance for a series of blocks.
2907	Ok((actions, ApprovalCheckResult::Accepted))
2908}
2909
2910#[derive(Debug)]
2911enum ApprovalStateTransition {
2912	RemoteApproval(ValidatorIndex),
2913	LocalApproval(ValidatorIndex),
2914	WakeupProcessed,
2915}
2916
2917impl ApprovalStateTransition {
2918	fn validator_index(&self) -> Option<ValidatorIndex> {
2919		match *self {
2920			ApprovalStateTransition::RemoteApproval(v) |
2921			ApprovalStateTransition::LocalApproval(v) => Some(v),
2922			ApprovalStateTransition::WakeupProcessed => None,
2923		}
2924	}
2925
2926	fn is_local_approval(&self) -> bool {
2927		match *self {
2928			ApprovalStateTransition::RemoteApproval(_) => false,
2929			ApprovalStateTransition::LocalApproval(_) => true,
2930			ApprovalStateTransition::WakeupProcessed => false,
2931		}
2932	}
2933
2934	fn is_remote_approval(&self) -> bool {
2935		matches!(*self, ApprovalStateTransition::RemoteApproval(_))
2936	}
2937}
2938
2939// Advance the approval state, either by importing an approval vote which is already checked to be
2940// valid and corresponding to an assigned validator on the candidate and block, or by noting that
2941// there are no further wakeups or tranches needed. This updates the block entry and candidate entry
2942// as necessary and schedules any further wakeups.
2943async fn advance_approval_state<Sender>(
2944	sender: &mut Sender,
2945	state: &mut State,
2946	db: &mut OverlayedBackend<'_, impl Backend>,
2947	session_info_provider: &mut RuntimeInfo,
2948	metrics: &Metrics,
2949	mut block_entry: BlockEntry,
2950	candidate_hash: CandidateHash,
2951	mut candidate_entry: CandidateEntry,
2952	transition: ApprovalStateTransition,
2953	wakeups: &Wakeups,
2954) -> Vec<Action>
2955where
2956	Sender: SubsystemSender<RuntimeApiMessage>,
2957{
2958	let validator_index = transition.validator_index();
2959
2960	let already_approved_by = validator_index.as_ref().map(|v| candidate_entry.mark_approval(*v));
2961	let candidate_approved_in_block = block_entry.is_candidate_approved(&candidate_hash);
2962
2963	// Check for early exits.
2964	//
2965	// If the candidate was approved
2966	// but not the block, it means that we still need more approvals for the candidate under the
2967	// block.
2968	//
2969	// If the block was approved, but the validator hadn't approved it yet, we should still hold
2970	// onto the approval vote on-disk in case we restart and rebroadcast votes. Otherwise, our
2971	// assignment might manifest as a no-show.
2972	if !transition.is_local_approval() {
2973		// We don't store remote votes and there's nothing to store for processed wakeups,
2974		// so we can early exit as long at the candidate is already concluded under the
2975		// block i.e. we don't need more approvals.
2976		if candidate_approved_in_block {
2977			return Vec::new();
2978		}
2979	}
2980
2981	let mut actions = Vec::new();
2982	let block_hash = block_entry.block_hash();
2983	let block_number = block_entry.block_number();
2984	let session_index = block_entry.session();
2985	let para_id = candidate_entry.candidate_receipt().descriptor().para_id();
2986	let tick_now = state.clock.tick_now();
2987
2988	let (is_approved, status) = if let Some((approval_entry, status)) = state
2989		.approval_status(sender, session_info_provider, &block_entry, &candidate_entry)
2990		.await
2991	{
2992		let check = approval_checking::check_approval(
2993			&candidate_entry,
2994			approval_entry,
2995			status.required_tranches.clone(),
2996		);
2997		state.observe_assignment_gathering_status(
2998			&metrics,
2999			&status.required_tranches,
3000			block_hash,
3001			block_entry.block_number(),
3002			candidate_hash,
3003		);
3004
3005		// Check whether this is approved, while allowing a maximum
3006		// assignment tick of `now - APPROVAL_DELAY` - that is, that
3007		// all counted assignments are at least `APPROVAL_DELAY` ticks old.
3008		let is_approved = check.is_approved(tick_now.saturating_sub(APPROVAL_DELAY));
3009		if status.last_no_shows != 0 {
3010			metrics.on_observed_no_shows(status.last_no_shows);
3011			gum::trace!(
3012				target: LOG_TARGET,
3013				?candidate_hash,
3014				?block_hash,
3015				last_no_shows = ?status.last_no_shows,
3016				"Observed no_shows",
3017			);
3018		}
3019		if is_approved {
3020			gum::trace!(
3021				target: LOG_TARGET,
3022				?candidate_hash,
3023				?block_hash,
3024				"Candidate approved under block.",
3025			);
3026
3027			let no_shows = check.known_no_shows();
3028
3029			let was_block_approved = block_entry.is_fully_approved();
3030			block_entry.mark_approved_by_hash(&candidate_hash);
3031			let is_block_approved = block_entry.is_fully_approved();
3032
3033			if no_shows != 0 {
3034				metrics.on_no_shows(no_shows);
3035			}
3036			if check == Check::ApprovedOneThird {
3037				// No-shows are not counted when more than one third of validators approve a
3038				// candidate, so count candidates where more than one third of validators had to
3039				// approve it, this is indicative of something breaking.
3040				metrics.on_approved_by_one_third()
3041			}
3042
3043			metrics.on_candidate_approved(status.tranche_now as _);
3044
3045			if is_block_approved && !was_block_approved {
3046				metrics.on_block_approved(status.tranche_now as _);
3047				actions.push(Action::NoteApprovedInChainSelection(block_hash));
3048			}
3049
3050			db.write_block_entry(block_entry.into());
3051		} else if transition.is_local_approval() {
3052			// Local approvals always update the block_entry, so we need to flush it to
3053			// the database.
3054			db.write_block_entry(block_entry.into());
3055		}
3056
3057		(is_approved, status)
3058	} else {
3059		gum::warn!(
3060			target: LOG_TARGET,
3061			?candidate_hash,
3062			?block_hash,
3063			?validator_index,
3064			"No approval entry for approval under block",
3065		);
3066
3067		return Vec::new();
3068	};
3069
3070	{
3071		let approval_entry = candidate_entry
3072			.approval_entry_mut(&block_hash)
3073			.expect("Approval entry just fetched; qed");
3074
3075		let was_approved = approval_entry.is_approved();
3076		let newly_approved = is_approved && !was_approved;
3077
3078		if is_approved {
3079			approval_entry.mark_approved();
3080		}
3081		if newly_approved {
3082			state.record_no_shows(session_index, para_id.into(), &status.no_show_validators);
3083		}
3084		actions.extend(schedule_wakeup_action(
3085			&approval_entry,
3086			block_hash,
3087			block_number,
3088			candidate_hash,
3089			status.block_tick,
3090			tick_now,
3091			status.required_tranches,
3092		));
3093
3094		if is_approved && transition.is_remote_approval() {
3095			// Make sure we wake other blocks in case they have
3096			// a no-show that might be covered by this approval.
3097			for (fork_block_hash, fork_approval_entry) in candidate_entry
3098				.block_assignments
3099				.iter()
3100				.filter(|(hash, _)| **hash != block_hash)
3101			{
3102				let assigned_on_fork_block = validator_index
3103					.as_ref()
3104					.map(|validator_index| fork_approval_entry.is_assigned(*validator_index))
3105					.unwrap_or_default();
3106				if wakeups.wakeup_for(*fork_block_hash, candidate_hash).is_none() &&
3107					!fork_approval_entry.is_approved() &&
3108					assigned_on_fork_block
3109				{
3110					let fork_block_entry = db.load_block_entry(fork_block_hash);
3111					if let Ok(Some(fork_block_entry)) = fork_block_entry {
3112						actions.push(Action::ScheduleWakeup {
3113							block_hash: *fork_block_hash,
3114							block_number: fork_block_entry.block_number(),
3115							candidate_hash,
3116							// Schedule the wakeup next tick, since the assignment must be a
3117							// no-show, because there is no-wakeup scheduled.
3118							tick: tick_now + 1,
3119						})
3120					} else {
3121						gum::debug!(
3122							target: LOG_TARGET,
3123							?fork_block_entry,
3124							?fork_block_hash,
3125							"Failed to load block entry"
3126						)
3127					}
3128				}
3129			}
3130		}
3131		// We have no need to write the candidate entry if all of the following
3132		// is true:
3133		//
3134		// 1. This is not a local approval, as we don't store anything new in the approval entry.
3135		// 2. The candidate is not newly approved, as we haven't altered the approval entry's
3136		//    approved flag with `mark_approved` above.
3137		// 3. The approver, if any, had already approved the candidate, as we haven't altered the
3138		// bitfield.
3139		if transition.is_local_approval() || newly_approved || !already_approved_by.unwrap_or(true)
3140		{
3141			// In all other cases, we need to write the candidate entry.
3142			db.write_candidate_entry(candidate_entry);
3143		}
3144	}
3145
3146	actions
3147}
3148
3149fn should_trigger_assignment(
3150	approval_entry: &ApprovalEntry,
3151	candidate_entry: &CandidateEntry,
3152	required_tranches: RequiredTranches,
3153	tranche_now: DelayTranche,
3154) -> bool {
3155	match approval_entry.our_assignment() {
3156		None => false,
3157		Some(ref assignment) if assignment.triggered() => false,
3158		Some(ref assignment) if assignment.tranche() == 0 => true,
3159		Some(ref assignment) => {
3160			match required_tranches {
3161				RequiredTranches::All => !approval_checking::check_approval(
3162					&candidate_entry,
3163					&approval_entry,
3164					RequiredTranches::All,
3165				)
3166				// when all are required, we are just waiting for the first 1/3+
3167				.is_approved(Tick::max_value()),
3168				RequiredTranches::Pending { maximum_broadcast, clock_drift, .. } => {
3169					let drifted_tranche_now =
3170						tranche_now.saturating_sub(clock_drift as DelayTranche);
3171					assignment.tranche() <= maximum_broadcast &&
3172						assignment.tranche() <= drifted_tranche_now
3173				},
3174				RequiredTranches::Exact { .. } => {
3175					// indicates that no new assignments are needed at the moment.
3176					false
3177				},
3178			}
3179		},
3180	}
3181}
3182
3183async fn process_wakeup<Sender: SubsystemSender<RuntimeApiMessage>>(
3184	sender: &mut Sender,
3185	state: &mut State,
3186	db: &mut OverlayedBackend<'_, impl Backend>,
3187	session_info_provider: &mut RuntimeInfo,
3188	relay_block: Hash,
3189	candidate_hash: CandidateHash,
3190	metrics: &Metrics,
3191	wakeups: &Wakeups,
3192) -> SubsystemResult<Vec<Action>> {
3193	let block_entry = db.load_block_entry(&relay_block)?;
3194	let candidate_entry = db.load_candidate_entry(&candidate_hash)?;
3195
3196	// If either is not present, we have nothing to wakeup. Might have lost a race with finality
3197	let (mut block_entry, mut candidate_entry) = match (block_entry, candidate_entry) {
3198		(Some(b), Some(c)) => (b, c),
3199		_ => return Ok(Vec::new()),
3200	};
3201
3202	let (no_show_slots, needed_approvals) = match get_session_info_by_index(
3203		session_info_provider,
3204		sender,
3205		block_entry.block_hash(),
3206		block_entry.session(),
3207	)
3208	.await
3209	{
3210		Some(i) => (i.no_show_slots, i.needed_approvals),
3211		None => return Ok(Vec::new()),
3212	};
3213
3214	let block_tick = slot_number_to_tick(state.slot_duration_millis, block_entry.slot());
3215	let no_show_duration =
3216		slot_number_to_tick(state.slot_duration_millis, Slot::from(u64::from(no_show_slots)));
3217	let tranche_now = state.clock.tranche_now(state.slot_duration_millis, block_entry.slot());
3218
3219	gum::trace!(
3220		target: LOG_TARGET,
3221		tranche = tranche_now,
3222		?candidate_hash,
3223		block_hash = ?relay_block,
3224		"Processing wakeup",
3225	);
3226
3227	let (should_trigger, backing_group) = {
3228		let approval_entry = match candidate_entry.approval_entry(&relay_block) {
3229			Some(e) => e,
3230			None => return Ok(Vec::new()),
3231		};
3232
3233		let tranches_to_approve = approval_checking::tranches_to_approve(
3234			&approval_entry,
3235			candidate_entry.approvals(),
3236			tranche_now,
3237			block_tick,
3238			no_show_duration,
3239			needed_approvals as _,
3240		);
3241
3242		let should_trigger = should_trigger_assignment(
3243			&approval_entry,
3244			&candidate_entry,
3245			tranches_to_approve.required_tranches,
3246			tranche_now,
3247		);
3248
3249		(should_trigger, approval_entry.backing_group())
3250	};
3251
3252	gum::trace!(target: LOG_TARGET, "Wakeup processed. Should trigger: {}", should_trigger);
3253
3254	let mut actions = Vec::new();
3255	let candidate_receipt = candidate_entry.candidate_receipt().clone();
3256
3257	let maybe_cert = if should_trigger {
3258		let maybe_cert = {
3259			let approval_entry = candidate_entry
3260				.approval_entry_mut(&relay_block)
3261				.expect("should_trigger only true if this fetched earlier; qed");
3262
3263			approval_entry.trigger_our_assignment(state.clock.tick_now())
3264		};
3265
3266		db.write_candidate_entry(candidate_entry.clone());
3267
3268		maybe_cert
3269	} else {
3270		None
3271	};
3272
3273	if let Some((cert, val_index, tranche)) = maybe_cert {
3274		let indirect_cert =
3275			IndirectAssignmentCertV2 { block_hash: relay_block, validator: val_index, cert };
3276
3277		gum::trace!(
3278			target: LOG_TARGET,
3279			?candidate_hash,
3280			para_id = ?candidate_receipt.descriptor.para_id(),
3281			block_hash = ?relay_block,
3282			"Launching approval work.",
3283		);
3284
3285		let candidate_core_index = block_entry
3286			.candidates()
3287			.iter()
3288			.find_map(|(core_index, h)| (h == &candidate_hash).then_some(*core_index));
3289
3290		let claimed_core_indices = get_assignment_core_indices(&indirect_cert.cert.kind);
3291		match cores_to_candidate_indices(&claimed_core_indices, &block_entry) {
3292			Ok(claimed_candidate_indices) => {
3293				// Ensure we distribute multiple core assignments just once.
3294				let distribute_assignment = if claimed_candidate_indices.count_ones() > 1 {
3295					!block_entry.mark_assignment_distributed(claimed_candidate_indices.clone())
3296				} else {
3297					true
3298				};
3299				db.write_block_entry(block_entry.clone());
3300				actions.push(Action::LaunchApproval {
3301					claimed_candidate_indices,
3302					candidate_hash,
3303					indirect_cert,
3304					assignment_tranche: tranche,
3305					relay_block_hash: relay_block,
3306					session: block_entry.session(),
3307					candidate: candidate_receipt,
3308					backing_group,
3309					distribute_assignment,
3310					core_index: candidate_core_index,
3311				});
3312			},
3313			Err(err) => {
3314				// Never happens, it should only happen if no cores are claimed, which is a
3315				// bug.
3316				gum::warn!(
3317					target: LOG_TARGET,
3318					block_hash = ?relay_block,
3319					?err,
3320					"Failed to create assignment bitfield"
3321				);
3322			},
3323		};
3324	}
3325	// Although we checked approval earlier in this function,
3326	// this wakeup might have advanced the state to approved via
3327	// a no-show that was immediately covered and therefore
3328	// we need to check for that and advance the state on-disk.
3329	//
3330	// Note that this function also schedules a wakeup as necessary.
3331	actions.extend(
3332		advance_approval_state(
3333			sender,
3334			state,
3335			db,
3336			session_info_provider,
3337			metrics,
3338			block_entry,
3339			candidate_hash,
3340			candidate_entry,
3341			ApprovalStateTransition::WakeupProcessed,
3342			wakeups,
3343		)
3344		.await,
3345	);
3346
3347	Ok(actions)
3348}
3349
3350// Launch approval work, returning an `AbortHandle` which corresponds to the background task
3351// spawned. When the background work is no longer needed, the `AbortHandle` should be dropped
3352// to cancel the background work and any requests it has spawned.
3353#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)]
3354async fn launch_approval<
3355	Sender: SubsystemSender<RuntimeApiMessage>
3356		+ SubsystemSender<AvailabilityRecoveryMessage>
3357		+ SubsystemSender<DisputeCoordinatorMessage>
3358		+ SubsystemSender<CandidateValidationMessage>,
3359>(
3360	mut sender: Sender,
3361	spawn_handle: Arc<dyn overseer::gen::Spawner + 'static>,
3362	metrics: Metrics,
3363	session_index: SessionIndex,
3364	candidate: CandidateReceipt,
3365	validator_index: ValidatorIndex,
3366	block_hash: Hash,
3367	backing_group: GroupIndex,
3368	core_index: Option<CoreIndex>,
3369	retry: RetryApprovalInfo,
3370) -> SubsystemResult<RemoteHandle<ApprovalState>> {
3371	let (a_tx, a_rx) = oneshot::channel();
3372	let (code_tx, code_rx) = oneshot::channel();
3373
3374	// The background future returned by this function may
3375	// be dropped before completing. This guard is used to ensure that the approval
3376	// work is correctly counted as stale even if so.
3377	struct StaleGuard(Option<Metrics>);
3378
3379	impl StaleGuard {
3380		fn take(mut self) -> Metrics {
3381			self.0.take().expect(
3382				"
3383				consumed after take; so this cannot be called twice; \
3384				nothing in this function reaches into the struct to avoid this API; \
3385				qed
3386			",
3387			)
3388		}
3389	}
3390
3391	impl Drop for StaleGuard {
3392		fn drop(&mut self) {
3393			if let Some(metrics) = self.0.as_ref() {
3394				metrics.on_approval_stale();
3395			}
3396		}
3397	}
3398
3399	let candidate_hash = candidate.hash();
3400	let para_id = candidate.descriptor.para_id();
3401	let mut next_retry = None;
3402	gum::trace!(target: LOG_TARGET, ?candidate_hash, ?para_id, "Recovering data.");
3403
3404	let timer = metrics.time_recover_and_approve();
3405	sender
3406		.send_message(AvailabilityRecoveryMessage::RecoverAvailableData(
3407			candidate.clone(),
3408			session_index,
3409			Some(backing_group),
3410			core_index,
3411			a_tx,
3412		))
3413		.await;
3414
3415	sender
3416		.send_message(RuntimeApiMessage::Request(
3417			block_hash,
3418			RuntimeApiRequest::ValidationCodeByHash(
3419				candidate.descriptor.validation_code_hash(),
3420				code_tx,
3421			),
3422		))
3423		.await;
3424
3425	let candidate = candidate.clone();
3426	let metrics_guard = StaleGuard(Some(metrics));
3427	let background = async move {
3428		// Force the move of the timer into the background task.
3429		let _timer = timer;
3430		let available_data = match a_rx.await {
3431			Err(_) => return ApprovalState::failed(validator_index, candidate_hash),
3432			Ok(Ok(a)) => a,
3433			Ok(Err(e)) => {
3434				match &e {
3435					&RecoveryError::Unavailable => {
3436						gum::warn!(
3437							target: LOG_TARGET,
3438							?para_id,
3439							?candidate_hash,
3440							attempts_remaining = retry.attempts_remaining,
3441							"Data unavailable for candidate {:?}",
3442							(candidate_hash, candidate.descriptor.para_id()),
3443						);
3444						// Availability could fail if we did not discover much of the network, so
3445						// let's back off and order the subsystem to retry at a later point if the
3446						// approval is still needed, because no-show wasn't covered yet.
3447						if retry.attempts_remaining > 0 {
3448							Delay::new(retry.backoff).await;
3449							next_retry = Some(RetryApprovalInfo {
3450								candidate,
3451								backing_group,
3452								core_index,
3453								session_index,
3454								attempts_remaining: retry.attempts_remaining - 1,
3455								backoff: retry.backoff,
3456							});
3457						} else {
3458							next_retry = None;
3459						}
3460						metrics_guard.take().on_approval_unavailable();
3461					},
3462					&RecoveryError::ChannelClosed => {
3463						gum::warn!(
3464							target: LOG_TARGET,
3465							?para_id,
3466							?candidate_hash,
3467							"Channel closed while recovering data for candidate {:?}",
3468							(candidate_hash, candidate.descriptor.para_id()),
3469						);
3470						// do nothing. we'll just be a no-show and that'll cause others to rise up.
3471						metrics_guard.take().on_approval_unavailable();
3472					},
3473					&RecoveryError::Invalid => {
3474						gum::warn!(
3475							target: LOG_TARGET,
3476							?para_id,
3477							?candidate_hash,
3478							"Data recovery invalid for candidate {:?}",
3479							(candidate_hash, candidate.descriptor.para_id()),
3480						);
3481						issue_local_invalid_statement(
3482							&mut sender,
3483							session_index,
3484							candidate_hash,
3485							candidate.clone(),
3486						);
3487						metrics_guard.take().on_approval_invalid();
3488					},
3489				}
3490				return ApprovalState::failed_with_retry(
3491					validator_index,
3492					candidate_hash,
3493					next_retry,
3494				);
3495			},
3496		};
3497
3498		let validation_code = match code_rx.await {
3499			Err(_) => return ApprovalState::failed(validator_index, candidate_hash),
3500			Ok(Err(_)) => return ApprovalState::failed(validator_index, candidate_hash),
3501			Ok(Ok(Some(code))) => code,
3502			Ok(Ok(None)) => {
3503				gum::warn!(
3504					target: LOG_TARGET,
3505					"Validation code unavailable for block {:?} in the state of block {:?} (a recent descendant)",
3506					candidate.descriptor.relay_parent(),
3507					block_hash,
3508				);
3509
3510				// No dispute necessary, as this indicates that the chain is not behaving
3511				// according to expectations.
3512				metrics_guard.take().on_approval_unavailable();
3513				return ApprovalState::failed(validator_index, candidate_hash);
3514			},
3515		};
3516
3517		let (val_tx, val_rx) = oneshot::channel();
3518		sender
3519			.send_message(CandidateValidationMessage::ValidateFromExhaustive {
3520				validation_data: available_data.validation_data,
3521				validation_code,
3522				candidate_receipt: candidate.clone(),
3523				pov: available_data.pov,
3524				scheduling_session_index: session_index,
3525				exec_kind: PvfExecKind::Approval,
3526				response_sender: val_tx,
3527			})
3528			.await;
3529
3530		match val_rx.await {
3531			Err(_) => return ApprovalState::failed(validator_index, candidate_hash),
3532			Ok(Ok(ValidationResult::Valid(_, _))) => {
3533				// Validation checked out. Issue an approval command. If the underlying service is
3534				// unreachable, then there isn't anything we can do.
3535
3536				gum::trace!(target: LOG_TARGET, ?candidate_hash, ?para_id, "Candidate Valid");
3537
3538				let _ = metrics_guard.take();
3539				return ApprovalState::approved(validator_index, candidate_hash);
3540			},
3541			Ok(Ok(ValidationResult::Invalid(reason))) => {
3542				gum::warn!(
3543					target: LOG_TARGET,
3544					?reason,
3545					?candidate_hash,
3546					?para_id,
3547					"Detected invalid candidate as an approval checker.",
3548				);
3549
3550				issue_local_invalid_statement(
3551					&mut sender,
3552					session_index,
3553					candidate_hash,
3554					candidate.clone(),
3555				);
3556				metrics_guard.take().on_approval_invalid();
3557				return ApprovalState::failed(validator_index, candidate_hash);
3558			},
3559			Ok(Err(e)) => {
3560				gum::error!(
3561					target: LOG_TARGET,
3562					err = ?e,
3563					?candidate_hash,
3564					?para_id,
3565					"Failed to validate candidate due to internal error",
3566				);
3567				metrics_guard.take().on_approval_error();
3568				return ApprovalState::failed(validator_index, candidate_hash);
3569			},
3570		}
3571	};
3572	let (background, remote_handle) = background.remote_handle();
3573	spawn_handle.spawn("approval-checks", Some("approval-voting-subsystem"), Box::pin(background));
3574	Ok(remote_handle)
3575}
3576
3577// Issue and import a local approval vote. Should only be invoked after approval checks
3578// have been done.
3579#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)]
3580async fn issue_approval<
3581	Sender: SubsystemSender<RuntimeApiMessage>,
3582	ADSender: SubsystemSender<ApprovalDistributionMessage>,
3583>(
3584	sender: &mut Sender,
3585	approval_voting_sender: &mut ADSender,
3586	state: &mut State,
3587	db: &mut OverlayedBackend<'_, impl Backend>,
3588	session_info_provider: &mut RuntimeInfo,
3589	metrics: &Metrics,
3590	candidate_hash: CandidateHash,
3591	delayed_approvals_timers: &mut DelayedApprovalTimer,
3592	ApprovalVoteRequest { validator_index, block_hash }: ApprovalVoteRequest,
3593	wakeups: &Wakeups,
3594) -> SubsystemResult<Vec<Action>> {
3595	let mut block_entry = match db.load_block_entry(&block_hash)? {
3596		Some(b) => b,
3597		None => {
3598			// not a cause for alarm - just lost a race with pruning, most likely.
3599			metrics.on_approval_stale();
3600			return Ok(Vec::new());
3601		},
3602	};
3603
3604	let candidate_index = match block_entry.candidates().iter().position(|e| e.1 == candidate_hash)
3605	{
3606		None => {
3607			gum::warn!(
3608				target: LOG_TARGET,
3609				"Candidate hash {} is not present in the block entry's candidates for relay block {}",
3610				candidate_hash,
3611				block_entry.parent_hash(),
3612			);
3613
3614			metrics.on_approval_error();
3615			return Ok(Vec::new());
3616		},
3617		Some(idx) => idx,
3618	};
3619
3620	let candidate_hash = match block_entry.candidate(candidate_index as usize) {
3621		Some((_, h)) => *h,
3622		None => {
3623			gum::warn!(
3624				target: LOG_TARGET,
3625				"Received malformed request to approve out-of-bounds candidate index {} included at block {:?}",
3626				candidate_index,
3627				block_hash,
3628			);
3629
3630			metrics.on_approval_error();
3631			return Ok(Vec::new());
3632		},
3633	};
3634
3635	let candidate_entry = match db.load_candidate_entry(&candidate_hash)? {
3636		Some(c) => c,
3637		None => {
3638			gum::warn!(
3639				target: LOG_TARGET,
3640				"Missing entry for candidate index {} included at block {:?}",
3641				candidate_index,
3642				block_hash,
3643			);
3644
3645			metrics.on_approval_error();
3646			return Ok(Vec::new());
3647		},
3648	};
3649
3650	let session_info = match get_session_info_by_index(
3651		session_info_provider,
3652		sender,
3653		block_entry.parent_hash(),
3654		block_entry.session(),
3655	)
3656	.await
3657	{
3658		Some(s) => s,
3659		None => return Ok(Vec::new()),
3660	};
3661
3662	if block_entry
3663		.defer_candidate_signature(
3664			candidate_index as _,
3665			candidate_hash,
3666			compute_delayed_approval_sending_tick(
3667				state,
3668				&block_entry,
3669				&candidate_entry,
3670				session_info,
3671				&metrics,
3672			),
3673		)
3674		.is_some()
3675	{
3676		gum::error!(
3677			target: LOG_TARGET,
3678			?candidate_hash,
3679			?block_hash,
3680			validator_index = validator_index.0,
3681			"Possible bug, we shouldn't have to defer a candidate more than once",
3682		);
3683	}
3684
3685	gum::debug!(
3686		target: LOG_TARGET,
3687		?candidate_hash,
3688		?block_hash,
3689		validator_index = validator_index.0,
3690		"Ready to issue approval vote",
3691	);
3692
3693	let actions = advance_approval_state(
3694		sender,
3695		state,
3696		db,
3697		session_info_provider,
3698		metrics,
3699		block_entry,
3700		candidate_hash,
3701		candidate_entry,
3702		ApprovalStateTransition::LocalApproval(validator_index as _),
3703		wakeups,
3704	)
3705	.await;
3706
3707	if let Some(next_wakeup) = maybe_create_signature(
3708		db,
3709		session_info_provider,
3710		state,
3711		sender,
3712		approval_voting_sender,
3713		block_hash,
3714		validator_index,
3715		metrics,
3716	)
3717	.await?
3718	{
3719		delayed_approvals_timers.maybe_arm_timer(
3720			next_wakeup,
3721			state.clock.as_ref(),
3722			block_hash,
3723			validator_index,
3724		);
3725	}
3726	Ok(actions)
3727}
3728
3729// Create signature for the approved candidates pending signatures
3730#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)]
3731async fn maybe_create_signature<
3732	Sender: SubsystemSender<RuntimeApiMessage>,
3733	ADSender: SubsystemSender<ApprovalDistributionMessage>,
3734>(
3735	db: &mut OverlayedBackend<'_, impl Backend>,
3736	session_info_provider: &mut RuntimeInfo,
3737	state: &State,
3738	sender: &mut Sender,
3739	approval_voting_sender: &mut ADSender,
3740	block_hash: Hash,
3741	validator_index: ValidatorIndex,
3742	metrics: &Metrics,
3743) -> SubsystemResult<Option<Tick>> {
3744	let mut block_entry = match db.load_block_entry(&block_hash)? {
3745		Some(b) => b,
3746		None => {
3747			// not a cause for alarm - just lost a race with pruning, most likely.
3748			metrics.on_approval_stale();
3749			gum::debug!(
3750				target: LOG_TARGET,
3751				"Could not find block that needs signature {:}", block_hash
3752			);
3753			return Ok(None);
3754		},
3755	};
3756
3757	let approval_params = session_info_provider
3758		.get_session_info_by_index(sender, block_hash, block_entry.session())
3759		.await
3760		.map(|info| info.approval_voting_params)
3761		.unwrap_or_default();
3762
3763	gum::trace!(
3764		target: LOG_TARGET,
3765		"Candidates pending signatures {:}", block_entry.num_candidates_pending_signature()
3766	);
3767	let tick_now = state.clock.tick_now();
3768
3769	let (candidates_to_sign, sign_no_later_then) = block_entry
3770		.get_candidates_that_need_signature(tick_now, approval_params.max_approval_coalesce_count);
3771
3772	let (candidates_hashes, candidates_indices) = match candidates_to_sign {
3773		Some(candidates_to_sign) => candidates_to_sign,
3774		None => return Ok(sign_no_later_then),
3775	};
3776
3777	let session_info = match get_session_info_by_index(
3778		session_info_provider,
3779		sender,
3780		block_entry.parent_hash(),
3781		block_entry.session(),
3782	)
3783	.await
3784	{
3785		Some(s) => s,
3786		None => {
3787			metrics.on_approval_error();
3788			gum::error!(
3789				target: LOG_TARGET,
3790				"Could not retrieve the session"
3791			);
3792			return Ok(None);
3793		},
3794	};
3795
3796	let validator_pubkey = match session_info.validators.get(validator_index) {
3797		Some(p) => p,
3798		None => {
3799			gum::error!(
3800				target: LOG_TARGET,
3801				"Validator index {} out of bounds in session {}",
3802				validator_index.0,
3803				block_entry.session(),
3804			);
3805
3806			metrics.on_approval_error();
3807			return Ok(None);
3808		},
3809	};
3810
3811	let signature = match sign_approval(
3812		&state.keystore,
3813		&validator_pubkey,
3814		&candidates_hashes,
3815		block_entry.session(),
3816	) {
3817		Some(sig) => sig,
3818		None => {
3819			gum::error!(
3820				target: LOG_TARGET,
3821				validator_index = ?validator_index,
3822				session = ?block_entry.session(),
3823				"Could not issue approval signature. Assignment key present but not validator key?",
3824			);
3825
3826			metrics.on_approval_error();
3827			return Ok(None);
3828		},
3829	};
3830	metrics.on_approval_coalesce(candidates_hashes.len() as u32);
3831
3832	let candidate_entries = candidates_hashes
3833		.iter()
3834		.map(|candidate_hash| db.load_candidate_entry(candidate_hash))
3835		.collect::<SubsystemResult<Vec<Option<CandidateEntry>>>>()?;
3836
3837	for mut candidate_entry in candidate_entries {
3838		let approval_entry = candidate_entry.as_mut().and_then(|candidate_entry| {
3839			candidate_entry.approval_entry_mut(&block_entry.block_hash())
3840		});
3841
3842		match approval_entry {
3843			Some(approval_entry) => approval_entry.import_approval_sig(OurApproval {
3844				signature: signature.clone(),
3845				signed_candidates_indices: candidates_indices.clone(),
3846			}),
3847			None => {
3848				gum::error!(
3849					target: LOG_TARGET,
3850					candidate_entry = ?candidate_entry,
3851					"Candidate scheduled for signing approval entry should not be None"
3852				);
3853			},
3854		};
3855		candidate_entry.map(|candidate_entry| db.write_candidate_entry(candidate_entry));
3856	}
3857
3858	metrics.on_approval_produced();
3859
3860	approval_voting_sender.send_unbounded_message(ApprovalDistributionMessage::DistributeApproval(
3861		IndirectSignedApprovalVoteV2 {
3862			block_hash: block_entry.block_hash(),
3863			candidate_indices: candidates_indices,
3864			validator: validator_index,
3865			signature,
3866		},
3867	));
3868
3869	gum::trace!(
3870		target: LOG_TARGET,
3871		?block_hash,
3872		signed_candidates = ?block_entry.num_candidates_pending_signature(),
3873		"Issue approval votes",
3874	);
3875	block_entry.issued_approval();
3876	db.write_block_entry(block_entry.into());
3877	Ok(None)
3878}
3879
3880// Sign an approval vote. Fails if the key isn't present in the store.
3881fn sign_approval(
3882	keystore: &LocalKeystore,
3883	public: &ValidatorId,
3884	candidate_hashes: &[CandidateHash],
3885	session_index: SessionIndex,
3886) -> Option<ValidatorSignature> {
3887	let key = keystore.key_pair::<ValidatorPair>(public).ok().flatten()?;
3888
3889	let payload = ApprovalVoteMultipleCandidates(candidate_hashes).signing_payload(session_index);
3890
3891	Some(key.sign(&payload[..]))
3892}
3893
3894/// Send `IssueLocalStatement` to dispute-coordinator.
3895fn issue_local_invalid_statement<Sender>(
3896	sender: &mut Sender,
3897	session_index: SessionIndex,
3898	candidate_hash: CandidateHash,
3899	candidate: CandidateReceipt,
3900) where
3901	Sender: SubsystemSender<DisputeCoordinatorMessage>,
3902{
3903	// We need to send an unbounded message here to break a cycle:
3904	// DisputeCoordinatorMessage::IssueLocalStatement ->
3905	// ApprovalVotingMessage::GetApprovalSignaturesForCandidate.
3906	//
3907	// Use of unbounded _should_ be fine here as raising a dispute should be an
3908	// exceptional event. Even in case of bugs: There can be no more than
3909	// number of slots per block requests every block. Also for sending this
3910	// message a full recovery and validation procedure took place, which takes
3911	// longer than issuing a local statement + import.
3912	sender.send_unbounded_message(DisputeCoordinatorMessage::IssueLocalStatement(
3913		session_index,
3914		candidate_hash,
3915		candidate.clone(),
3916		false,
3917	));
3918}
3919
3920// Computes what is the latest tick we can send an approval
3921fn compute_delayed_approval_sending_tick(
3922	state: &State,
3923	block_entry: &BlockEntry,
3924	candidate_entry: &CandidateEntry,
3925	session_info: &SessionInfo,
3926	metrics: &Metrics,
3927) -> Tick {
3928	let current_block_tick = slot_number_to_tick(state.slot_duration_millis, block_entry.slot());
3929	let assignment_tranche = candidate_entry
3930		.approval_entry(&block_entry.block_hash())
3931		.and_then(|approval_entry| approval_entry.our_assignment())
3932		.map(|our_assignment| our_assignment.tranche())
3933		.unwrap_or_default();
3934
3935	let assignment_triggered_tick = current_block_tick + assignment_tranche as Tick;
3936
3937	let no_show_duration_ticks = slot_number_to_tick(
3938		state.slot_duration_millis,
3939		Slot::from(u64::from(session_info.no_show_slots)),
3940	);
3941	let tick_now = state.clock.tick_now();
3942
3943	let sign_no_later_than = min(
3944		tick_now + MAX_APPROVAL_COALESCE_WAIT_TICKS as Tick,
3945		// We don't want to accidentally cause no-shows, so if we are past
3946		// the second half of the no show time, force the sending of the
3947		// approval immediately.
3948		assignment_triggered_tick + no_show_duration_ticks / 2,
3949	);
3950
3951	metrics.on_delayed_approval(sign_no_later_than.checked_sub(tick_now).unwrap_or_default());
3952	sign_no_later_than
3953}