referrerpolicy=no-referrer-when-downgrade

polkadot_node_core_approval_voting/
persisted_entries.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//! Entries pertaining to approval which need to be persisted.
18//!
19//! The actual persisting of data is handled by the `approval_db` module.
20//! Within that context, things are plain-old-data. Within this module,
21//! data and logic are intertwined.
22
23use itertools::Itertools;
24use polkadot_node_primitives::approval::{
25	v1::{DelayTranche, RelayVRFStory},
26	v2::{AssignmentCertV2, CandidateBitfield},
27};
28use polkadot_primitives::{
29	BlockNumber, CandidateHash, CandidateIndex, CandidateReceiptV2 as CandidateReceipt, CoreIndex,
30	GroupIndex, Hash, SessionIndex, ValidatorIndex, ValidatorSignature,
31};
32use sp_consensus_slots::Slot;
33
34use bitvec::{order::Lsb0 as BitOrderLsb0, slice::BitSlice};
35use std::collections::BTreeMap;
36
37use crate::approval_db::v2::Bitfield;
38
39use super::criteria::OurAssignment;
40
41use polkadot_node_primitives::approval::time::Tick;
42
43/// Metadata regarding a specific tranche of assignments for a specific candidate.
44#[derive(Debug, Clone, PartialEq)]
45pub struct TrancheEntry {
46	tranche: DelayTranche,
47	// Assigned validators, and the instant we received their assignment, rounded
48	// to the nearest tick.
49	assignments: Vec<(ValidatorIndex, Tick)>,
50}
51
52impl TrancheEntry {
53	/// Get the tranche of this entry.
54	pub fn tranche(&self) -> DelayTranche {
55		self.tranche
56	}
57
58	/// Get the assignments for this entry.
59	pub fn assignments(&self) -> &[(ValidatorIndex, Tick)] {
60		&self.assignments
61	}
62}
63
64impl From<crate::approval_db::v2::TrancheEntry> for TrancheEntry {
65	fn from(entry: crate::approval_db::v2::TrancheEntry) -> Self {
66		TrancheEntry {
67			tranche: entry.tranche,
68			assignments: entry.assignments.into_iter().map(|(v, t)| (v, t.into())).collect(),
69		}
70	}
71}
72
73impl From<TrancheEntry> for crate::approval_db::v2::TrancheEntry {
74	fn from(entry: TrancheEntry) -> Self {
75		Self {
76			tranche: entry.tranche,
77			assignments: entry.assignments.into_iter().map(|(v, t)| (v, t.into())).collect(),
78		}
79	}
80}
81
82impl From<crate::approval_db::v3::OurApproval> for OurApproval {
83	fn from(approval: crate::approval_db::v3::OurApproval) -> Self {
84		Self {
85			signature: approval.signature,
86			signed_candidates_indices: approval.signed_candidates_indices,
87		}
88	}
89}
90impl From<OurApproval> for crate::approval_db::v3::OurApproval {
91	fn from(approval: OurApproval) -> Self {
92		Self {
93			signature: approval.signature,
94			signed_candidates_indices: approval.signed_candidates_indices,
95		}
96	}
97}
98
99/// Metadata about our approval signature
100#[derive(Debug, Clone, PartialEq)]
101pub struct OurApproval {
102	/// The signature for the candidates hashes pointed by indices.
103	pub signature: ValidatorSignature,
104	/// The indices of the candidates signed in this approval.
105	pub signed_candidates_indices: CandidateBitfield,
106}
107
108impl OurApproval {
109	/// Converts a ValidatorSignature to an OurApproval.
110	/// It used in converting the database from v1 to latest.
111	pub fn from_v1(value: ValidatorSignature, candidate_index: CandidateIndex) -> Self {
112		Self { signature: value, signed_candidates_indices: candidate_index.into() }
113	}
114
115	/// Converts a ValidatorSignature to an OurApproval.
116	/// It used in converting the database from v2 to latest.
117	pub fn from_v2(value: ValidatorSignature, candidate_index: CandidateIndex) -> Self {
118		Self::from_v1(value, candidate_index)
119	}
120}
121/// Metadata regarding approval of a particular candidate within the context of some
122/// particular block.
123#[derive(Debug, Clone, PartialEq)]
124pub struct ApprovalEntry {
125	tranches: Vec<TrancheEntry>,
126	backing_group: GroupIndex,
127	our_assignment: Option<OurAssignment>,
128	our_approval_sig: Option<OurApproval>,
129	// `n_validators` bits.
130	assigned_validators: Bitfield,
131	approved: bool,
132}
133
134impl ApprovalEntry {
135	/// Convenience constructor
136	pub fn new(
137		tranches: Vec<TrancheEntry>,
138		backing_group: GroupIndex,
139		our_assignment: Option<OurAssignment>,
140		our_approval_sig: Option<OurApproval>,
141		// `n_validators` bits.
142		assigned_validators: Bitfield,
143		approved: bool,
144	) -> Self {
145		Self {
146			tranches,
147			backing_group,
148			our_assignment,
149			our_approval_sig,
150			assigned_validators,
151			approved,
152		}
153	}
154
155	// Access our assignment for this approval entry.
156	pub fn our_assignment(&self) -> Option<&OurAssignment> {
157		self.our_assignment.as_ref()
158	}
159
160	// Note that our assignment is triggered. No-op if already triggered.
161	pub fn trigger_our_assignment(
162		&mut self,
163		tick_now: Tick,
164	) -> Option<(AssignmentCertV2, ValidatorIndex, DelayTranche)> {
165		let our = self.our_assignment.as_mut().and_then(|a| {
166			if a.triggered() {
167				return None
168			}
169			a.mark_triggered();
170
171			Some(a.clone())
172		});
173
174		our.map(|a| {
175			self.import_assignment(a.tranche(), a.validator_index(), tick_now, false);
176
177			(a.cert().clone(), a.validator_index(), a.tranche())
178		})
179	}
180
181	/// Import our local approval vote signature for this candidate.
182	pub fn import_approval_sig(&mut self, approval_sig: OurApproval) {
183		self.our_approval_sig = Some(approval_sig);
184	}
185
186	/// Whether a validator is already assigned.
187	pub fn is_assigned(&self, validator_index: ValidatorIndex) -> bool {
188		self.assigned_validators
189			.get(validator_index.0 as usize)
190			.map(|b| *b)
191			.unwrap_or(false)
192	}
193
194	/// Import an assignment. No-op if already assigned on the same tranche.
195	pub fn import_assignment(
196		&mut self,
197		tranche: DelayTranche,
198		validator_index: ValidatorIndex,
199		tick_now: Tick,
200		is_duplicate: bool,
201	) {
202		// linear search probably faster than binary. not many tranches typically.
203		let idx = match self.tranches.iter().position(|t| t.tranche >= tranche) {
204			Some(pos) => {
205				if self.tranches[pos].tranche > tranche {
206					self.tranches.insert(pos, TrancheEntry { tranche, assignments: Vec::new() });
207				}
208
209				pos
210			},
211			None => {
212				self.tranches.push(TrancheEntry { tranche, assignments: Vec::new() });
213
214				self.tranches.len() - 1
215			},
216		};
217		// At restart we might have duplicate assignments because approval-distribution is not
218		// persistent across restarts, so avoid adding duplicates.
219		// We already know if we have seen an assignment from this validator and since this
220		// function is on the hot path we can avoid iterating through tranches by using
221		// !is_duplicate to determine if it is already present in the vector and does not need
222		// adding.
223		if !is_duplicate {
224			self.tranches[idx].assignments.push((validator_index, tick_now));
225		}
226		self.assigned_validators.set(validator_index.0 as _, true);
227	}
228
229	// Produce a bitvec indicating the assignments of all validators up to and
230	// including `tranche`.
231	pub fn assignments_up_to(&self, tranche: DelayTranche) -> Bitfield {
232		self.tranches.iter().take_while(|e| e.tranche <= tranche).fold(
233			bitvec::bitvec![u8, BitOrderLsb0; 0; self.assigned_validators.len()],
234			|mut a, e| {
235				for &(v, _) in &e.assignments {
236					a.set(v.0 as _, true);
237				}
238
239				a
240			},
241		)
242	}
243
244	/// Whether the approval entry is approved
245	pub fn is_approved(&self) -> bool {
246		self.approved
247	}
248
249	/// Mark the approval entry as approved.
250	pub fn mark_approved(&mut self) {
251		self.approved = true;
252	}
253
254	/// Access the tranches.
255	pub fn tranches(&self) -> &[TrancheEntry] {
256		&self.tranches
257	}
258
259	/// Get the number of validators in this approval entry.
260	pub fn n_validators(&self) -> usize {
261		self.assigned_validators.len()
262	}
263
264	/// Get the number of assignments by validators, including the local validator.
265	pub fn n_assignments(&self) -> usize {
266		self.assigned_validators.count_ones()
267	}
268
269	/// Get the backing group index of the approval entry.
270	pub fn backing_group(&self) -> GroupIndex {
271		self.backing_group
272	}
273
274	/// Get the assignment cert & approval signature.
275	///
276	/// The approval signature will only be `Some` if the assignment is too.
277	pub fn local_statements(&self) -> (Option<OurAssignment>, Option<OurApproval>) {
278		let approval_sig = self.our_approval_sig.clone();
279		if let Some(our_assignment) = self.our_assignment.as_ref().filter(|a| a.triggered()) {
280			(Some(our_assignment.clone()), approval_sig)
281		} else {
282			(None, None)
283		}
284	}
285
286	// Convert an ApprovalEntry from v1 version to latest version
287	pub fn from_v1(
288		value: crate::approval_db::v1::ApprovalEntry,
289		candidate_index: CandidateIndex,
290	) -> Self {
291		ApprovalEntry {
292			tranches: value.tranches.into_iter().map(|tranche| tranche.into()).collect(),
293			backing_group: value.backing_group,
294			our_assignment: value.our_assignment.map(|assignment| assignment.into()),
295			our_approval_sig: value
296				.our_approval_sig
297				.map(|sig| OurApproval::from_v1(sig, candidate_index)),
298			assigned_validators: value.assignments,
299			approved: value.approved,
300		}
301	}
302
303	// Convert an ApprovalEntry from v1 version to latest version
304	pub fn from_v2(
305		value: crate::approval_db::v2::ApprovalEntry,
306		candidate_index: CandidateIndex,
307	) -> Self {
308		ApprovalEntry {
309			tranches: value.tranches.into_iter().map(|tranche| tranche.into()).collect(),
310			backing_group: value.backing_group,
311			our_assignment: value.our_assignment.map(|assignment| assignment.into()),
312			our_approval_sig: value
313				.our_approval_sig
314				.map(|sig| OurApproval::from_v2(sig, candidate_index)),
315			assigned_validators: value.assigned_validators,
316			approved: value.approved,
317		}
318	}
319}
320
321impl From<crate::approval_db::v3::ApprovalEntry> for ApprovalEntry {
322	fn from(entry: crate::approval_db::v3::ApprovalEntry) -> Self {
323		ApprovalEntry {
324			tranches: entry.tranches.into_iter().map(Into::into).collect(),
325			backing_group: entry.backing_group,
326			our_assignment: entry.our_assignment.map(Into::into),
327			our_approval_sig: entry.our_approval_sig.map(Into::into),
328			assigned_validators: entry.assigned_validators,
329			approved: entry.approved,
330		}
331	}
332}
333
334impl From<ApprovalEntry> for crate::approval_db::v3::ApprovalEntry {
335	fn from(entry: ApprovalEntry) -> Self {
336		Self {
337			tranches: entry.tranches.into_iter().map(Into::into).collect(),
338			backing_group: entry.backing_group,
339			our_assignment: entry.our_assignment.map(Into::into),
340			our_approval_sig: entry.our_approval_sig.map(Into::into),
341			assigned_validators: entry.assigned_validators,
342			approved: entry.approved,
343		}
344	}
345}
346
347/// Metadata regarding approval of a particular candidate.
348#[derive(Debug, Clone, PartialEq)]
349pub struct CandidateEntry {
350	pub candidate: CandidateReceipt,
351	pub session: SessionIndex,
352	// Assignments are based on blocks, so we need to track assignments separately
353	// based on the block we are looking at.
354	pub block_assignments: BTreeMap<Hash, ApprovalEntry>,
355	pub approvals: Bitfield,
356}
357
358impl CandidateEntry {
359	/// Access the bit-vec of approvals.
360	pub fn approvals(&self) -> &BitSlice<u8, BitOrderLsb0> {
361		&self.approvals
362	}
363
364	/// Note that a given validator has approved. Return the previous approval state.
365	pub fn mark_approval(&mut self, validator: ValidatorIndex) -> bool {
366		let prev = self.has_approved(validator);
367		self.approvals.set(validator.0 as usize, true);
368		prev
369	}
370
371	/// Query whether a given validator has approved the candidate.
372	pub fn has_approved(&self, validator: ValidatorIndex) -> bool {
373		self.approvals.get(validator.0 as usize).map(|b| *b).unwrap_or(false)
374	}
375
376	/// Get the candidate receipt.
377	pub fn candidate_receipt(&self) -> &CandidateReceipt {
378		&self.candidate
379	}
380
381	/// Get the approval entry, mutably, for this candidate under a specific block.
382	pub fn approval_entry_mut(&mut self, block_hash: &Hash) -> Option<&mut ApprovalEntry> {
383		self.block_assignments.get_mut(block_hash)
384	}
385
386	/// Get the approval entry for this candidate under a specific block.
387	pub fn approval_entry(&self, block_hash: &Hash) -> Option<&ApprovalEntry> {
388		self.block_assignments.get(block_hash)
389	}
390
391	/// Convert a CandidateEntry from a v1 to its latest equivalent.
392	pub fn from_v1(
393		value: crate::approval_db::v1::CandidateEntry,
394		candidate_index: CandidateIndex,
395	) -> Self {
396		Self {
397			approvals: value.approvals,
398			block_assignments: value
399				.block_assignments
400				.into_iter()
401				.map(|(h, ae)| (h, ApprovalEntry::from_v1(ae, candidate_index)))
402				.collect(),
403			candidate: value.candidate,
404			session: value.session,
405		}
406	}
407
408	/// Convert a CandidateEntry from a v2 to its latest equivalent.
409	pub fn from_v2(
410		value: crate::approval_db::v2::CandidateEntry,
411		candidate_index: CandidateIndex,
412	) -> Self {
413		Self {
414			approvals: value.approvals,
415			block_assignments: value
416				.block_assignments
417				.into_iter()
418				.map(|(h, ae)| (h, ApprovalEntry::from_v2(ae, candidate_index)))
419				.collect(),
420			candidate: value.candidate,
421			session: value.session,
422		}
423	}
424}
425
426impl From<crate::approval_db::v3::CandidateEntry> for CandidateEntry {
427	fn from(entry: crate::approval_db::v3::CandidateEntry) -> Self {
428		CandidateEntry {
429			candidate: entry.candidate,
430			session: entry.session,
431			block_assignments: entry
432				.block_assignments
433				.into_iter()
434				.map(|(h, ae)| (h, ae.into()))
435				.collect(),
436			approvals: entry.approvals,
437		}
438	}
439}
440
441impl From<CandidateEntry> for crate::approval_db::v3::CandidateEntry {
442	fn from(entry: CandidateEntry) -> Self {
443		Self {
444			candidate: entry.candidate,
445			session: entry.session,
446			block_assignments: entry
447				.block_assignments
448				.into_iter()
449				.map(|(h, ae)| (h, ae.into()))
450				.collect(),
451			approvals: entry.approvals,
452		}
453	}
454}
455
456/// Metadata regarding approval of a particular block, by way of approval of the
457/// candidates contained within it.
458#[derive(Debug, Clone, PartialEq)]
459pub struct BlockEntry {
460	block_hash: Hash,
461	parent_hash: Hash,
462	block_number: BlockNumber,
463	session: SessionIndex,
464	slot: Slot,
465	relay_vrf_story: RelayVRFStory,
466	// The candidates included as-of this block and the index of the core they are
467	// leaving.
468	candidates: Vec<(CoreIndex, CandidateHash)>,
469	// A bitfield where the i'th bit corresponds to the i'th candidate in `candidates`.
470	// The i'th bit is `true` iff the candidate has been approved in the context of this
471	// block. The block can be considered approved if the bitfield has all bits set to `true`.
472	pub approved_bitfield: Bitfield,
473	pub children: Vec<Hash>,
474	// A list of candidates we have checked, but didn't not sign and
475	// advertise the vote yet.
476	candidates_pending_signature: BTreeMap<CandidateIndex, CandidateSigningContext>,
477	// A list of assignments for which we already distributed the assignment.
478	// We use this to ensure we don't distribute multiple core assignments twice as we track
479	// individual wakeups for each core.
480	distributed_assignments: Bitfield,
481}
482
483#[derive(Debug, Clone, PartialEq)]
484pub struct CandidateSigningContext {
485	pub candidate_hash: CandidateHash,
486	pub sign_no_later_than_tick: Tick,
487}
488
489impl BlockEntry {
490	/// Mark a candidate as fully approved in the bitfield.
491	pub fn mark_approved_by_hash(&mut self, candidate_hash: &CandidateHash) {
492		if let Some(p) = self.candidates.iter().position(|(_, h)| h == candidate_hash) {
493			self.approved_bitfield.set(p, true);
494		}
495	}
496
497	/// Whether a candidate is approved in the bitfield.
498	pub fn is_candidate_approved(&self, candidate_hash: &CandidateHash) -> bool {
499		self.candidates
500			.iter()
501			.position(|(_, h)| h == candidate_hash)
502			.and_then(|p| self.approved_bitfield.get(p).map(|b| *b))
503			.unwrap_or(false)
504	}
505
506	/// Whether the block entry is fully approved.
507	pub fn is_fully_approved(&self) -> bool {
508		self.approved_bitfield.all()
509	}
510
511	/// Iterate over all unapproved candidates.
512	pub fn unapproved_candidates(&self) -> impl Iterator<Item = CandidateHash> + '_ {
513		self.approved_bitfield.iter().enumerate().filter_map(move |(i, a)| {
514			if !*a {
515				Some(self.candidates[i].1)
516			} else {
517				None
518			}
519		})
520	}
521
522	/// Get the slot of the block.
523	pub fn slot(&self) -> Slot {
524		self.slot
525	}
526
527	/// Get the relay-vrf-story of the block.
528	pub fn relay_vrf_story(&self) -> RelayVRFStory {
529		self.relay_vrf_story.clone()
530	}
531
532	/// Get the session index of the block.
533	pub fn session(&self) -> SessionIndex {
534		self.session
535	}
536
537	/// Get the i'th candidate.
538	pub fn candidate(&self, i: usize) -> Option<&(CoreIndex, CandidateHash)> {
539		self.candidates.get(i)
540	}
541
542	/// Access the underlying candidates as a slice.
543	pub fn candidates(&self) -> &[(CoreIndex, CandidateHash)] {
544		&self.candidates
545	}
546
547	/// Access the block number of the block entry.
548	pub fn block_number(&self) -> BlockNumber {
549		self.block_number
550	}
551
552	/// Access the block hash of the block entry.
553	pub fn block_hash(&self) -> Hash {
554		self.block_hash
555	}
556
557	/// Access the parent hash of the block entry.
558	pub fn parent_hash(&self) -> Hash {
559		self.parent_hash
560	}
561
562	/// Mark distributed assignment for many candidate indices.
563	/// Returns `true` if an assignment was already distributed for the `candidates`.
564	pub fn mark_assignment_distributed(&mut self, candidates: CandidateBitfield) -> bool {
565		let bitfield = candidates.into_inner();
566		let total_one_bits = self.distributed_assignments.count_ones();
567
568		let new_len = std::cmp::max(self.distributed_assignments.len(), bitfield.len());
569		self.distributed_assignments.resize(new_len, false);
570		self.distributed_assignments |= bitfield;
571
572		// If an operation did not change our current bitfield, we return true.
573		let distributed = total_one_bits == self.distributed_assignments.count_ones();
574
575		distributed
576	}
577
578	/// Defer signing and issuing an approval for a candidate no later than the specified tick
579	pub fn defer_candidate_signature(
580		&mut self,
581		candidate_index: CandidateIndex,
582		candidate_hash: CandidateHash,
583		sign_no_later_than_tick: Tick,
584	) -> Option<CandidateSigningContext> {
585		self.candidates_pending_signature.insert(
586			candidate_index,
587			CandidateSigningContext { candidate_hash, sign_no_later_than_tick },
588		)
589	}
590
591	/// Returns the number of candidates waiting for an approval to be issued.
592	pub fn num_candidates_pending_signature(&self) -> usize {
593		self.candidates_pending_signature.len()
594	}
595
596	/// Return if we have candidates waiting for signature to be issued
597	pub fn has_candidates_pending_signature(&self) -> bool {
598		!self.candidates_pending_signature.is_empty()
599	}
600
601	/// Returns true if candidate hash is in the queue for a signature.
602	pub fn candidate_is_pending_signature(&self, candidate_hash: CandidateHash) -> bool {
603		self.candidates_pending_signature
604			.values()
605			.any(|context| context.candidate_hash == candidate_hash)
606	}
607
608	/// Candidate hashes  for candidates pending signatures
609	fn candidate_hashes_pending_signature(&self) -> Vec<CandidateHash> {
610		self.candidates_pending_signature
611			.values()
612			.map(|unsigned_approval| unsigned_approval.candidate_hash)
613			.collect()
614	}
615
616	/// Candidate indices for candidates pending signature
617	fn candidate_indices_pending_signature(&self) -> Option<CandidateBitfield> {
618		self.candidates_pending_signature
619			.keys()
620			.map(|val| *val)
621			.collect_vec()
622			.try_into()
623			.ok()
624	}
625
626	/// Returns a list of candidates hashes that need need signature created at the current tick:
627	/// This might happen in other of the two reasons:
628	/// 1. We queued more than max_approval_coalesce_count candidates.
629	/// 2. We have candidates that waiting in the queue past their `sign_no_later_than_tick`
630	///
631	/// Additionally, we also return the first tick when we will have to create a signature,
632	/// so that the caller can arm the timer if it is not already armed.
633	pub fn get_candidates_that_need_signature(
634		&self,
635		tick_now: Tick,
636		max_approval_coalesce_count: u32,
637	) -> (Option<(Vec<CandidateHash>, CandidateBitfield)>, Option<Tick>) {
638		let sign_no_later_than_tick = self
639			.candidates_pending_signature
640			.values()
641			.min_by(|a, b| a.sign_no_later_than_tick.cmp(&b.sign_no_later_than_tick))
642			.map(|val| val.sign_no_later_than_tick);
643
644		if let Some(sign_no_later_than_tick) = sign_no_later_than_tick {
645			if sign_no_later_than_tick <= tick_now ||
646				self.num_candidates_pending_signature() >= max_approval_coalesce_count as usize
647			{
648				(
649					self.candidate_indices_pending_signature().and_then(|candidate_indices| {
650						Some((self.candidate_hashes_pending_signature(), candidate_indices))
651					}),
652					Some(sign_no_later_than_tick),
653				)
654			} else {
655				// We can still wait for other candidates to queue in, so just make sure
656				// we wake up at the tick we have to sign the longest waiting candidate.
657				(Default::default(), Some(sign_no_later_than_tick))
658			}
659		} else {
660			// No cached candidates, nothing to do here, this just means the timer fired,
661			// but the signatures were already sent because we gathered more than
662			// max_approval_coalesce_count.
663			(Default::default(), sign_no_later_than_tick)
664		}
665	}
666
667	/// Clears the candidates pending signature because the approval was issued.
668	pub fn issued_approval(&mut self) {
669		self.candidates_pending_signature.clear();
670	}
671}
672
673impl From<crate::approval_db::v3::BlockEntry> for BlockEntry {
674	fn from(entry: crate::approval_db::v3::BlockEntry) -> Self {
675		BlockEntry {
676			block_hash: entry.block_hash,
677			parent_hash: entry.parent_hash,
678			block_number: entry.block_number,
679			session: entry.session,
680			slot: entry.slot,
681			relay_vrf_story: RelayVRFStory(entry.relay_vrf_story),
682			candidates: entry.candidates,
683			approved_bitfield: entry.approved_bitfield,
684			children: entry.children,
685			candidates_pending_signature: entry
686				.candidates_pending_signature
687				.into_iter()
688				.map(|(candidate_index, signing_context)| (candidate_index, signing_context.into()))
689				.collect(),
690			distributed_assignments: entry.distributed_assignments,
691		}
692	}
693}
694
695impl From<crate::approval_db::v1::BlockEntry> for BlockEntry {
696	fn from(entry: crate::approval_db::v1::BlockEntry) -> Self {
697		BlockEntry {
698			block_hash: entry.block_hash,
699			parent_hash: entry.parent_hash,
700			block_number: entry.block_number,
701			session: entry.session,
702			slot: entry.slot,
703			relay_vrf_story: RelayVRFStory(entry.relay_vrf_story),
704			candidates: entry.candidates,
705			approved_bitfield: entry.approved_bitfield,
706			children: entry.children,
707			distributed_assignments: Default::default(),
708			candidates_pending_signature: Default::default(),
709		}
710	}
711}
712
713impl From<crate::approval_db::v2::BlockEntry> for BlockEntry {
714	fn from(entry: crate::approval_db::v2::BlockEntry) -> Self {
715		BlockEntry {
716			block_hash: entry.block_hash,
717			parent_hash: entry.parent_hash,
718			block_number: entry.block_number,
719			session: entry.session,
720			slot: entry.slot,
721			relay_vrf_story: RelayVRFStory(entry.relay_vrf_story),
722			candidates: entry.candidates,
723			approved_bitfield: entry.approved_bitfield,
724			children: entry.children,
725			distributed_assignments: entry.distributed_assignments,
726			candidates_pending_signature: Default::default(),
727		}
728	}
729}
730
731impl From<BlockEntry> for crate::approval_db::v3::BlockEntry {
732	fn from(entry: BlockEntry) -> Self {
733		Self {
734			block_hash: entry.block_hash,
735			parent_hash: entry.parent_hash,
736			block_number: entry.block_number,
737			session: entry.session,
738			slot: entry.slot,
739			relay_vrf_story: entry.relay_vrf_story.0,
740			candidates: entry.candidates,
741			approved_bitfield: entry.approved_bitfield,
742			children: entry.children,
743			candidates_pending_signature: entry
744				.candidates_pending_signature
745				.into_iter()
746				.map(|(candidate_index, signing_context)| (candidate_index, signing_context.into()))
747				.collect(),
748			distributed_assignments: entry.distributed_assignments,
749		}
750	}
751}
752
753impl From<crate::approval_db::v3::CandidateSigningContext> for CandidateSigningContext {
754	fn from(signing_context: crate::approval_db::v3::CandidateSigningContext) -> Self {
755		Self {
756			candidate_hash: signing_context.candidate_hash,
757			sign_no_later_than_tick: signing_context.sign_no_later_than_tick.into(),
758		}
759	}
760}
761
762impl From<CandidateSigningContext> for crate::approval_db::v3::CandidateSigningContext {
763	fn from(signing_context: CandidateSigningContext) -> Self {
764		Self {
765			candidate_hash: signing_context.candidate_hash,
766			sign_no_later_than_tick: signing_context.sign_no_later_than_tick.into(),
767		}
768	}
769}