1#![warn(missing_docs)]
25
26use self::metrics::Metrics;
27use futures::{select, FutureExt as _};
28use itertools::Itertools;
29use net_protocol::peer_set::{ProtocolVersion, ValidationVersion};
30use polkadot_node_network_protocol::{
31 self as net_protocol, filter_by_peer_version,
32 grid_topology::{RandomRouting, RequiredRouting, SessionGridTopologies, SessionGridTopology},
33 peer_set::MAX_NOTIFICATION_SIZE,
34 v3 as protocol_v3, PeerId, UnifiedReputationChange as Rep, ValidationProtocols, View,
35};
36use polkadot_node_primitives::{
37 approval::{
38 criteria::{AssignmentCriteria, InvalidAssignment},
39 time::{Clock, ClockExt, SystemClock, TICK_TOO_FAR_IN_FUTURE},
40 v1::{BlockApprovalMeta, DelayTranche, RelayVRFStory},
41 v2::{
42 AsBitIndex, AssignmentCertKindV2, CandidateBitfield, IndirectAssignmentCertV2,
43 IndirectSignedApprovalVoteV2,
44 },
45 },
46 DISPUTE_WINDOW,
47};
48use polkadot_node_subsystem::{
49 messages::{
50 ApprovalDistributionMessage, ApprovalVotingMessage, CheckedIndirectAssignment,
51 CheckedIndirectSignedApprovalVote, NetworkBridgeEvent, NetworkBridgeTxMessage,
52 RuntimeApiMessage,
53 },
54 overseer, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError,
55};
56use polkadot_node_subsystem_util::{
57 reputation::{ReputationAggregator, REPUTATION_CHANGE_INTERVAL},
58 runtime::{Config as RuntimeInfoConfig, ExtendedSessionInfo, RuntimeInfo},
59};
60use polkadot_primitives::{
61 BlockNumber, CandidateHash, CandidateIndex, CoreIndex, DisputeStatement, GroupIndex, Hash,
62 SessionIndex, Slot, ValidDisputeStatementKind, ValidatorIndex, ValidatorSignature,
63 MAX_COALESCE_APPROVALS,
64};
65use rand::{CryptoRng, Rng, SeedableRng};
66use sp_core::{bounded::BoundedVec, ConstU32};
67use std::{
68 collections::{hash_map, BTreeMap, HashMap, HashSet, VecDeque},
69 sync::Arc,
70 time::Duration,
71};
72
73pub mod metrics;
75
76#[cfg(test)]
77mod tests;
78
79const LOG_TARGET: &str = "parachain::approval-distribution";
80
81const COST_UNEXPECTED_MESSAGE: Rep =
82 Rep::CostMinor("Peer sent an out-of-view assignment or approval");
83const COST_DUPLICATE_MESSAGE: Rep = Rep::CostMinorRepeated("Peer sent identical messages");
84const COST_ASSIGNMENT_TOO_FAR_IN_THE_FUTURE: Rep =
85 Rep::CostMinor("The vote was valid but too far in the future");
86const COST_INVALID_MESSAGE: Rep = Rep::CostMajor("The vote was bad");
87const COST_OVERSIZED_BITFIELD: Rep = Rep::CostMajor("Oversized certificate or candidate bitfield");
88
89const BENEFIT_VALID_MESSAGE: Rep = Rep::BenefitMinor("Peer sent a valid message");
90const BENEFIT_VALID_MESSAGE_FIRST: Rep =
91 Rep::BenefitMinorFirst("Valid message with new information");
92
93const MAX_BITFIELD_SIZE: usize = 500;
95
96pub struct ApprovalDistribution {
98 metrics: Metrics,
99 slot_duration_millis: u64,
100 clock: Arc<dyn Clock + Send + Sync>,
101 assignment_criteria: Arc<dyn AssignmentCriteria + Send + Sync>,
102}
103
104#[derive(Default)]
107struct RecentlyOutdated {
108 buf: VecDeque<Hash>,
109}
110
111impl RecentlyOutdated {
112 fn note_outdated(&mut self, hash: Hash) {
113 const MAX_BUF_LEN: usize = 20;
114
115 self.buf.push_back(hash);
116
117 while self.buf.len() > MAX_BUF_LEN {
118 let _ = self.buf.pop_front();
119 }
120 }
121
122 fn is_recent_outdated(&self, hash: &Hash) -> bool {
123 self.buf.contains(hash)
124 }
125}
126
127struct ApprovalRouting {
129 required_routing: RequiredRouting,
130 local: bool,
131 random_routing: RandomRouting,
132 peers_randomly_routed: Vec<PeerId>,
133}
134
135impl ApprovalRouting {
136 fn mark_randomly_sent(&mut self, peer: PeerId) {
137 self.random_routing.inc_sent();
138 self.peers_randomly_routed.push(peer);
139 }
140}
141
142struct ApprovalEntry {
145 assignment: IndirectAssignmentCertV2,
147 assignment_claimed_candidates: CandidateBitfield,
149 approvals: HashMap<CandidateBitfield, IndirectSignedApprovalVoteV2>,
151 validator_index: ValidatorIndex,
153 routing_info: ApprovalRouting,
155}
156
157#[derive(Debug)]
158enum ApprovalEntryError {
159 InvalidValidatorIndex,
160 CandidateIndexOutOfBounds,
161 InvalidCandidateIndex,
162 DuplicateApproval,
163 UnknownAssignment,
164}
165
166impl ApprovalEntry {
167 pub fn new(
168 assignment: IndirectAssignmentCertV2,
169 candidates: CandidateBitfield,
170 routing_info: ApprovalRouting,
171 ) -> ApprovalEntry {
172 Self {
173 validator_index: assignment.validator,
174 assignment,
175 approvals: HashMap::new(),
176 assignment_claimed_candidates: candidates,
177 routing_info,
178 }
179 }
180
181 pub fn create_assignment_knowledge(&self, block_hash: Hash) -> (MessageSubject, MessageKind) {
183 (
184 MessageSubject(
185 block_hash,
186 self.assignment_claimed_candidates.clone(),
187 self.validator_index,
188 ),
189 MessageKind::Assignment,
190 )
191 }
192
193 pub fn routing_info_mut(&mut self) -> &mut ApprovalRouting {
195 &mut self.routing_info
196 }
197
198 pub fn routing_info(&self) -> &ApprovalRouting {
200 &self.routing_info
201 }
202
203 pub fn update_required_routing(&mut self, required_routing: RequiredRouting) {
205 self.routing_info.required_routing = required_routing;
206 }
207
208 pub fn includes_approval_candidates(&self, approval: &IndirectSignedApprovalVoteV2) -> bool {
210 for candidate_index in approval.candidate_indices.iter_ones() {
211 if self.assignment_claimed_candidates.bit_at((candidate_index).as_bit_index()) {
212 return true;
213 }
214 }
215 return false;
216 }
217
218 pub fn note_approval(
221 &mut self,
222 approval: IndirectSignedApprovalVoteV2,
223 ) -> Result<(), ApprovalEntryError> {
224 if self.validator_index != approval.validator {
229 return Err(ApprovalEntryError::InvalidValidatorIndex);
230 }
231
232 if !self.includes_approval_candidates(&approval) {
234 return Err(ApprovalEntryError::InvalidCandidateIndex);
235 }
236
237 if self.approvals.contains_key(&approval.candidate_indices) {
238 return Err(ApprovalEntryError::DuplicateApproval);
239 }
240
241 self.approvals.insert(approval.candidate_indices.clone(), approval.clone());
242 Ok(())
243 }
244
245 pub fn assignment(&self) -> (IndirectAssignmentCertV2, CandidateBitfield) {
247 (self.assignment.clone(), self.assignment_claimed_candidates.clone())
248 }
249
250 pub fn approvals(&self) -> Vec<IndirectSignedApprovalVoteV2> {
252 self.approvals.values().cloned().collect::<Vec<_>>()
253 }
254
255 pub fn validator_index(&self) -> ValidatorIndex {
257 self.validator_index
258 }
259}
260
261struct PeerEntry {
263 pub view: View,
264 pub version: ProtocolVersion,
265}
266
267#[derive(Clone)]
289struct AggressionConfig {
290 l1_threshold: Option<BlockNumber>,
292 l2_threshold: Option<BlockNumber>,
295 resend_unfinalized_period: Option<BlockNumber>,
298}
299
300impl AggressionConfig {
301 fn should_trigger_aggression(&self, age: BlockNumber) -> bool {
303 if let Some(t) = self.l1_threshold {
304 age >= t
305 } else if let Some(t) = self.resend_unfinalized_period {
306 age > 0 && age.is_multiple_of(t)
307 } else {
308 false
309 }
310 }
311}
312
313impl Default for AggressionConfig {
314 fn default() -> Self {
315 AggressionConfig {
316 l1_threshold: Some(16),
317 l2_threshold: Some(64),
318 resend_unfinalized_period: Some(8),
319 }
320 }
321}
322
323#[derive(PartialEq)]
324enum Resend {
325 Yes,
326 No,
327}
328
329#[derive(Default)]
334pub struct State {
335 blocks_by_number: BTreeMap<BlockNumber, Vec<Hash>>,
337 blocks: HashMap<Hash, BlockEntry>,
338
339 pending_known: HashMap<Hash, Vec<(PeerId, PendingMessage)>>,
347
348 peer_views: HashMap<PeerId, PeerEntry>,
350
351 topologies: SessionGridTopologies,
353
354 recent_outdated_blocks: RecentlyOutdated,
356
357 aggression_config: AggressionConfig,
359
360 approval_checking_lag: BlockNumber,
362
363 reputation: ReputationAggregator,
365
366 slot_duration_millis: u64,
368}
369
370#[derive(Debug, Clone, Copy, PartialEq, Eq)]
371enum MessageKind {
372 Assignment,
373 Approval,
374}
375
376#[derive(Debug, Clone, Hash, PartialEq, Eq)]
380struct MessageSubject(Hash, pub CandidateBitfield, ValidatorIndex);
381
382#[derive(Debug, Clone, Default)]
383struct Knowledge {
384 known_messages: HashMap<MessageSubject, MessageKind>,
388}
389
390impl Knowledge {
391 fn contains(&self, message: &MessageSubject, kind: MessageKind) -> bool {
392 match (kind, self.known_messages.get(message)) {
393 (_, None) => false,
394 (MessageKind::Assignment, Some(_)) => true,
395 (MessageKind::Approval, Some(MessageKind::Assignment)) => false,
396 (MessageKind::Approval, Some(MessageKind::Approval)) => true,
397 }
398 }
399
400 fn insert(&mut self, message: MessageSubject, kind: MessageKind) -> bool {
401 let mut success = match self.known_messages.entry(message.clone()) {
402 hash_map::Entry::Vacant(vacant) => {
403 vacant.insert(kind);
404 true
407 },
408 hash_map::Entry::Occupied(mut occupied) => match (*occupied.get(), kind) {
409 (MessageKind::Assignment, MessageKind::Assignment) => false,
410 (MessageKind::Approval, MessageKind::Approval) => false,
411 (MessageKind::Approval, MessageKind::Assignment) => false,
412 (MessageKind::Assignment, MessageKind::Approval) => {
413 *occupied.get_mut() = MessageKind::Approval;
414 true
415 },
416 },
417 };
418
419 if kind == MessageKind::Assignment && success && message.1.count_ones() > 1 {
423 for candidate_index in message.1.iter_ones() {
424 success = success &&
425 self.insert(
426 MessageSubject(
427 message.0,
428 vec![candidate_index as u32].try_into().expect("Non-empty vec; qed"),
429 message.2,
430 ),
431 kind,
432 );
433 }
434 }
435 success
436 }
437}
438
439#[derive(Debug, Clone, Default)]
441struct PeerKnowledge {
442 sent: Knowledge,
444 received: Knowledge,
446}
447
448impl PeerKnowledge {
449 fn contains(&self, message: &MessageSubject, kind: MessageKind) -> bool {
450 self.sent.contains(message, kind) || self.received.contains(message, kind)
451 }
452
453 fn generate_assignments_keys(
456 approval: &IndirectSignedApprovalVoteV2,
457 ) -> Vec<(MessageSubject, MessageKind)> {
458 approval
459 .candidate_indices
460 .iter_ones()
461 .map(|candidate_index| {
462 (
463 MessageSubject(
464 approval.block_hash,
465 (candidate_index as CandidateIndex).into(),
466 approval.validator,
467 ),
468 MessageKind::Assignment,
469 )
470 })
471 .collect_vec()
472 }
473
474 fn generate_approval_key(
476 approval: &IndirectSignedApprovalVoteV2,
477 ) -> (MessageSubject, MessageKind) {
478 (
479 MessageSubject(
480 approval.block_hash,
481 approval.candidate_indices.clone(),
482 approval.validator,
483 ),
484 MessageKind::Approval,
485 )
486 }
487}
488
489struct BlockEntry {
491 known_by: HashMap<PeerId, PeerKnowledge>,
494 number: BlockNumber,
496 parent_hash: Hash,
498 knowledge: Knowledge,
500 candidates: Vec<CandidateEntry>,
502 candidates_metadata: Vec<(CandidateHash, CoreIndex, GroupIndex)>,
504 session: SessionIndex,
506 approval_entries: HashMap<(ValidatorIndex, CandidateBitfield), ApprovalEntry>,
509 vrf_story: RelayVRFStory,
511 slot: Slot,
513 last_resent_at_block_number: Option<u32>,
515}
516
517impl BlockEntry {
518 pub fn known_by(&self) -> Vec<PeerId> {
520 self.known_by.keys().cloned().collect::<Vec<_>>()
521 }
522
523 pub fn insert_approval_entry(&mut self, entry: ApprovalEntry) -> &mut ApprovalEntry {
524 for claimed_candidate_index in entry.assignment_claimed_candidates.iter_ones() {
528 match self.candidates.get_mut(claimed_candidate_index) {
529 Some(candidate_entry) => {
530 candidate_entry
531 .assignments
532 .entry(entry.validator_index())
533 .or_insert(entry.assignment_claimed_candidates.clone());
534 },
535 None => {
536 gum::warn!(
539 target: LOG_TARGET,
540 hash = ?entry.assignment.block_hash,
541 ?claimed_candidate_index,
542 "Missing candidate entry on `import_and_circulate_assignment`",
543 );
544 },
545 };
546 }
547
548 self.approval_entries
549 .entry((entry.validator_index, entry.assignment_claimed_candidates.clone()))
550 .or_insert(entry)
551 }
552
553 pub fn contains_candidates(&self, candidate_indices: &CandidateBitfield) -> bool {
555 candidate_indices
556 .iter_ones()
557 .all(|candidate_index| self.candidates.get(candidate_index as usize).is_some())
558 }
559
560 pub fn note_approval(
566 &mut self,
567 approval: IndirectSignedApprovalVoteV2,
568 ) -> Result<(RequiredRouting, HashSet<PeerId>), ApprovalEntryError> {
569 let mut required_routing: Option<RequiredRouting> = None;
570 let mut peers_randomly_routed_to = HashSet::new();
571
572 if self.candidates.len() < approval.candidate_indices.len() as usize {
573 return Err(ApprovalEntryError::CandidateIndexOutOfBounds);
574 }
575
576 let covered_assignments_bitfields: HashSet<CandidateBitfield> = approval
578 .candidate_indices
579 .iter_ones()
580 .filter_map(|candidate_index| {
581 self.candidates.get_mut(candidate_index).map_or(None, |candidate_entry| {
582 candidate_entry.assignments.get(&approval.validator).cloned()
583 })
584 })
585 .collect();
586
587 for assignment_bitfield in covered_assignments_bitfields {
589 if let Some(approval_entry) =
590 self.approval_entries.get_mut(&(approval.validator, assignment_bitfield))
591 {
592 approval_entry.note_approval(approval.clone())?;
593 peers_randomly_routed_to
594 .extend(approval_entry.routing_info().peers_randomly_routed.iter());
595
596 if let Some(current_required_routing) = required_routing {
597 required_routing = Some(
598 current_required_routing
599 .combine(approval_entry.routing_info().required_routing),
600 );
601 } else {
602 required_routing = Some(approval_entry.routing_info().required_routing)
603 }
604 }
605 }
606
607 if let Some(required_routing) = required_routing {
608 Ok((required_routing, peers_randomly_routed_to))
609 } else {
610 Err(ApprovalEntryError::UnknownAssignment)
611 }
612 }
613
614 pub fn approval_votes(
616 &self,
617 candidate_index: CandidateIndex,
618 ) -> Vec<IndirectSignedApprovalVoteV2> {
619 let result: Option<
620 HashMap<(ValidatorIndex, CandidateBitfield), IndirectSignedApprovalVoteV2>,
621 > = self.candidates.get(candidate_index as usize).map(|candidate_entry| {
622 candidate_entry
623 .assignments
624 .iter()
625 .filter_map(|(validator, assignment_bitfield)| {
626 self.approval_entries.get(&(*validator, assignment_bitfield.clone()))
627 })
628 .flat_map(|approval_entry| {
629 approval_entry
630 .approvals
631 .clone()
632 .into_iter()
633 .filter(|(approved_candidates, _)| {
634 approved_candidates.bit_at(candidate_index.as_bit_index())
635 })
636 .map(|(approved_candidates, vote)| {
637 ((approval_entry.validator_index, approved_candidates), vote)
638 })
639 })
640 .collect()
641 });
642
643 result.map(|result| result.into_values().collect_vec()).unwrap_or_default()
644 }
645}
646
647#[derive(Debug, Default)]
651struct CandidateEntry {
652 assignments: HashMap<ValidatorIndex, CandidateBitfield>,
655}
656
657#[derive(Debug, Clone, PartialEq)]
658enum MessageSource {
659 Peer(PeerId),
660 Local,
661}
662
663#[derive(Debug)]
665enum InvalidAssignmentError {
666 #[allow(dead_code)]
668 CryptoCheckFailed(InvalidAssignment),
669 NoClaimedCandidates,
671 #[allow(dead_code)]
673 ClaimedInvalidCandidateIndex {
674 claimed_index: usize,
675 max_index: usize,
676 },
677 OversizedClaimedBitfield,
679 #[allow(dead_code)]
681 SessionInfoNotFound(polkadot_node_subsystem_util::runtime::Error),
682}
683
684#[derive(Debug)]
686enum InvalidVoteError {
687 CandidateIndexOutOfBounds,
689 CandidateHashNotFound,
691 ValidatorIndexOutOfBounds,
693 InvalidSignature,
695 TooManyCandidates,
697 #[allow(dead_code)]
699 SessionInfoNotFound(polkadot_node_subsystem_util::runtime::Error),
700}
701
702impl MessageSource {
703 fn peer_id(&self) -> Option<PeerId> {
704 match self {
705 Self::Peer(id) => Some(*id),
706 Self::Local => None,
707 }
708 }
709}
710
711enum PendingMessage {
712 Assignment(IndirectAssignmentCertV2, CandidateBitfield),
713 Approval(IndirectSignedApprovalVoteV2),
714}
715
716#[overseer::contextbounds(ApprovalDistribution, prefix = self::overseer)]
717impl State {
718 pub fn with_config(slot_duration_millis: u64) -> Self {
720 Self { slot_duration_millis, ..Default::default() }
721 }
722
723 async fn handle_network_msg<
724 N: overseer::SubsystemSender<NetworkBridgeTxMessage>,
725 A: overseer::SubsystemSender<ApprovalVotingMessage>,
726 RA: overseer::SubsystemSender<RuntimeApiMessage>,
727 >(
728 &mut self,
729 approval_voting_sender: &mut A,
730 network_sender: &mut N,
731 runtime_api_sender: &mut RA,
732 metrics: &Metrics,
733 event: NetworkBridgeEvent<net_protocol::ApprovalDistributionMessage>,
734 rng: &mut (impl CryptoRng + Rng),
735 assignment_criteria: &(impl AssignmentCriteria + ?Sized),
736 clock: &(impl Clock + ?Sized),
737 session_info_provider: &mut RuntimeInfo,
738 ) {
739 match event {
740 NetworkBridgeEvent::PeerConnected(peer_id, role, version, authority_ids) => {
741 gum::trace!(target: LOG_TARGET, ?peer_id, ?role, ?authority_ids, "Peer connected");
742 if let Some(authority_ids) = authority_ids {
743 self.topologies.update_authority_ids(peer_id, &authority_ids);
744 }
745 self.peer_views
747 .entry(peer_id)
748 .or_insert(PeerEntry { view: Default::default(), version });
749 },
750 NetworkBridgeEvent::PeerDisconnected(peer_id) => {
751 gum::trace!(target: LOG_TARGET, ?peer_id, "Peer disconnected");
752 self.peer_views.remove(&peer_id);
753 self.blocks.iter_mut().for_each(|(_hash, entry)| {
754 entry.known_by.remove(&peer_id);
755 })
756 },
757 NetworkBridgeEvent::NewGossipTopology(topology) => {
758 self.handle_new_session_topology(
759 network_sender,
760 topology.session,
761 topology.topology,
762 topology.local_index,
763 )
764 .await;
765 },
766 NetworkBridgeEvent::PeerViewChange(peer_id, view) => {
767 self.handle_peer_view_change(network_sender, metrics, peer_id, view, rng).await;
768 },
769 NetworkBridgeEvent::OurViewChange(view) => {
770 gum::trace!(target: LOG_TARGET, ?view, "Own view change");
771 for head in view.iter() {
772 if !self.blocks.contains_key(head) {
773 self.pending_known.entry(*head).or_default();
774 }
775 }
776
777 self.pending_known.retain(|h, _| {
778 let live = view.contains(h);
779 if !live {
780 gum::trace!(
781 target: LOG_TARGET,
782 block_hash = ?h,
783 "Cleaning up stale pending messages",
784 );
785 }
786 live
787 });
788 },
789 NetworkBridgeEvent::PeerMessage(peer_id, message) => {
790 self.process_incoming_peer_message(
791 approval_voting_sender,
792 network_sender,
793 runtime_api_sender,
794 metrics,
795 peer_id,
796 message,
797 rng,
798 assignment_criteria,
799 clock,
800 session_info_provider,
801 )
802 .await;
803 },
804 NetworkBridgeEvent::UpdatedAuthorityIds(peer_id, authority_ids) => {
805 gum::debug!(target: LOG_TARGET, ?peer_id, ?authority_ids, "Update Authority Ids");
806 if self.topologies.update_authority_ids(peer_id, &authority_ids) {
809 if let Some(PeerEntry { view, version }) = self.peer_views.get(&peer_id) {
810 let intersection = self
811 .blocks_by_number
812 .iter()
813 .filter(|(block_number, _)| *block_number > &view.finalized_number)
814 .flat_map(|(_, hashes)| {
815 hashes.iter().filter(|hash| {
816 self.blocks
817 .get(&hash)
818 .map(|block| block.known_by.get(&peer_id).is_some())
819 .unwrap_or_default()
820 })
821 });
822 let view_intersection =
823 View::new(intersection.cloned(), view.finalized_number);
824 Self::unify_with_peer(
825 network_sender,
826 metrics,
827 &mut self.blocks,
828 &self.topologies,
829 self.peer_views.len(),
830 peer_id,
831 *version,
832 view_intersection,
833 rng,
834 true,
835 )
836 .await;
837 }
838 }
839 },
840 }
841 }
842
843 async fn handle_new_blocks<
844 N: overseer::SubsystemSender<NetworkBridgeTxMessage>,
845 A: overseer::SubsystemSender<ApprovalVotingMessage>,
846 RA: overseer::SubsystemSender<RuntimeApiMessage>,
847 >(
848 &mut self,
849 approval_voting_sender: &mut A,
850 network_sender: &mut N,
851 runtime_api_sender: &mut RA,
852 metrics: &Metrics,
853 metas: Vec<BlockApprovalMeta>,
854 rng: &mut (impl CryptoRng + Rng),
855 assignment_criteria: &(impl AssignmentCriteria + ?Sized),
856 clock: &(impl Clock + ?Sized),
857 session_info_provider: &mut RuntimeInfo,
858 ) {
859 let mut new_hashes = HashSet::new();
860
861 gum::debug!(
862 target: LOG_TARGET,
863 "Got new blocks {:?}",
864 metas.iter().map(|m| (m.hash, m.number)).collect::<Vec<_>>(),
865 );
866
867 for meta in metas {
868 match self.blocks.entry(meta.hash) {
869 hash_map::Entry::Vacant(entry) => {
870 let candidates_count = meta.candidates.len();
871 let mut candidates = Vec::with_capacity(candidates_count);
872 candidates.resize_with(candidates_count, Default::default);
873
874 entry.insert(BlockEntry {
875 known_by: HashMap::new(),
876 number: meta.number,
877 parent_hash: meta.parent_hash,
878 knowledge: Knowledge::default(),
879 candidates,
880 session: meta.session,
881 approval_entries: HashMap::new(),
882 candidates_metadata: meta.candidates,
883 vrf_story: meta.vrf_story,
884 slot: meta.slot,
885 last_resent_at_block_number: None,
886 });
887
888 self.topologies.inc_session_refs(meta.session);
889
890 new_hashes.insert(meta.hash);
891
892 self.blocks_by_number.entry(meta.number).or_default().push(meta.hash);
895 },
896 _ => continue,
897 }
898 }
899
900 {
901 for (peer_id, PeerEntry { view, version }) in self.peer_views.iter() {
902 let intersection = view.iter().filter(|h| new_hashes.contains(h));
903 let view_intersection = View::new(intersection.cloned(), view.finalized_number);
904 Self::unify_with_peer(
905 network_sender,
906 metrics,
907 &mut self.blocks,
908 &self.topologies,
909 self.peer_views.len(),
910 *peer_id,
911 *version,
912 view_intersection,
913 rng,
914 false,
915 )
916 .await;
917 }
918
919 let pending_now_known = self
920 .pending_known
921 .keys()
922 .filter(|k| self.blocks.contains_key(k))
923 .copied()
924 .collect::<Vec<_>>();
925
926 let to_import = pending_now_known
927 .into_iter()
928 .inspect(|h| {
929 gum::trace!(
930 target: LOG_TARGET,
931 block_hash = ?h,
932 "Extracting pending messages for new block"
933 )
934 })
935 .filter_map(|k| self.pending_known.remove(&k))
936 .flatten()
937 .collect::<Vec<_>>();
938
939 if !to_import.is_empty() {
940 gum::debug!(
941 target: LOG_TARGET,
942 num = to_import.len(),
943 "Processing pending assignment/approvals",
944 );
945
946 let _timer = metrics.time_import_pending_now_known();
947
948 for (peer_id, message) in to_import {
949 match message {
950 PendingMessage::Assignment(assignment, claimed_indices) => {
951 self.import_and_circulate_assignment(
952 approval_voting_sender,
953 network_sender,
954 runtime_api_sender,
955 metrics,
956 MessageSource::Peer(peer_id),
957 assignment,
958 claimed_indices,
959 rng,
960 assignment_criteria,
961 clock,
962 session_info_provider,
963 )
964 .await;
965 },
966 PendingMessage::Approval(approval_vote) => {
967 self.import_and_circulate_approval(
968 approval_voting_sender,
969 network_sender,
970 runtime_api_sender,
971 metrics,
972 MessageSource::Peer(peer_id),
973 approval_vote,
974 session_info_provider,
975 )
976 .await;
977 },
978 }
979 }
980 }
981 }
982
983 self.enable_aggression(network_sender, Resend::Yes, metrics).await;
984 }
985
986 async fn handle_new_session_topology<N: overseer::SubsystemSender<NetworkBridgeTxMessage>>(
987 &mut self,
988 network_sender: &mut N,
989 session: SessionIndex,
990 topology: SessionGridTopology,
991 local_index: Option<ValidatorIndex>,
992 ) {
993 if local_index.is_none() {
994 return;
996 }
997
998 self.topologies.insert_topology(session, topology, local_index);
999 let topology = self.topologies.get_topology(session).expect("just inserted above; qed");
1000
1001 adjust_required_routing_and_propagate(
1002 network_sender,
1003 &mut self.blocks,
1004 &self.topologies,
1005 |block_entry| block_entry.session == session,
1006 |required_routing, local, validator_index| {
1007 if required_routing == &RequiredRouting::PendingTopology {
1008 topology
1009 .local_grid_neighbors()
1010 .required_routing_by_index(*validator_index, local)
1011 } else {
1012 *required_routing
1013 }
1014 },
1015 &self.peer_views,
1016 )
1017 .await;
1018 }
1019
1020 async fn process_incoming_assignments<A, N, R, RA>(
1021 &mut self,
1022 approval_voting_sender: &mut A,
1023 network_sender: &mut N,
1024 runtime_api_sender: &mut RA,
1025 metrics: &Metrics,
1026 peer_id: PeerId,
1027 assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)>,
1028 rng: &mut R,
1029 assignment_criteria: &(impl AssignmentCriteria + ?Sized),
1030 clock: &(impl Clock + ?Sized),
1031 session_info_provider: &mut RuntimeInfo,
1032 ) where
1033 A: overseer::SubsystemSender<ApprovalVotingMessage>,
1034 N: overseer::SubsystemSender<NetworkBridgeTxMessage>,
1035 RA: overseer::SubsystemSender<RuntimeApiMessage>,
1036 R: CryptoRng + Rng,
1037 {
1038 for (assignment, claimed_indices) in assignments {
1039 if let Some(pending) = self.pending_known.get_mut(&assignment.block_hash) {
1040 let block_hash = &assignment.block_hash;
1041 let validator_index = assignment.validator;
1042
1043 gum::trace!(
1044 target: LOG_TARGET,
1045 %peer_id,
1046 ?block_hash,
1047 ?claimed_indices,
1048 ?validator_index,
1049 "Pending assignment",
1050 );
1051
1052 pending.push((peer_id, PendingMessage::Assignment(assignment, claimed_indices)));
1053
1054 continue;
1055 }
1056
1057 self.import_and_circulate_assignment(
1058 approval_voting_sender,
1059 network_sender,
1060 runtime_api_sender,
1061 metrics,
1062 MessageSource::Peer(peer_id),
1063 assignment,
1064 claimed_indices,
1065 rng,
1066 assignment_criteria,
1067 clock,
1068 session_info_provider,
1069 )
1070 .await;
1071 }
1072 }
1073
1074 async fn process_incoming_approvals<
1076 N: overseer::SubsystemSender<NetworkBridgeTxMessage>,
1077 A: overseer::SubsystemSender<ApprovalVotingMessage>,
1078 RA: overseer::SubsystemSender<RuntimeApiMessage>,
1079 >(
1080 &mut self,
1081 approval_voting_sender: &mut A,
1082 network_sender: &mut N,
1083 runtime_api_sender: &mut RA,
1084 metrics: &Metrics,
1085 peer_id: PeerId,
1086 approvals: Vec<IndirectSignedApprovalVoteV2>,
1087 session_info_provider: &mut RuntimeInfo,
1088 ) {
1089 gum::trace!(
1090 target: LOG_TARGET,
1091 peer_id = %peer_id,
1092 num = approvals.len(),
1093 "Processing approvals from a peer",
1094 );
1095 for approval_vote in approvals.into_iter() {
1096 if let Some(pending) = self.pending_known.get_mut(&approval_vote.block_hash) {
1097 let block_hash = approval_vote.block_hash;
1098 let validator_index = approval_vote.validator;
1099
1100 gum::trace!(
1101 target: LOG_TARGET,
1102 %peer_id,
1103 ?block_hash,
1104 ?validator_index,
1105 "Pending assignment candidates {:?}",
1106 approval_vote.candidate_indices,
1107 );
1108
1109 pending.push((peer_id, PendingMessage::Approval(approval_vote)));
1110
1111 continue;
1112 }
1113
1114 self.import_and_circulate_approval(
1115 approval_voting_sender,
1116 network_sender,
1117 runtime_api_sender,
1118 metrics,
1119 MessageSource::Peer(peer_id),
1120 approval_vote,
1121 session_info_provider,
1122 )
1123 .await;
1124 }
1125 }
1126
1127 async fn process_incoming_peer_message<A, N, RA, R>(
1128 &mut self,
1129 approval_voting_sender: &mut A,
1130 network_sender: &mut N,
1131 runtime_api_sender: &mut RA,
1132 metrics: &Metrics,
1133 peer_id: PeerId,
1134 msg: ValidationProtocols<protocol_v3::ApprovalDistributionMessage>,
1135 rng: &mut R,
1136 assignment_criteria: &(impl AssignmentCriteria + ?Sized),
1137 clock: &(impl Clock + ?Sized),
1138 session_info_provider: &mut RuntimeInfo,
1139 ) where
1140 A: overseer::SubsystemSender<ApprovalVotingMessage>,
1141 N: overseer::SubsystemSender<NetworkBridgeTxMessage>,
1142 RA: overseer::SubsystemSender<RuntimeApiMessage>,
1143 R: CryptoRng + Rng,
1144 {
1145 match msg {
1146 ValidationProtocols::V3(protocol_v3::ApprovalDistributionMessage::Assignments(
1147 assignments,
1148 )) => {
1149 gum::trace!(
1150 target: LOG_TARGET,
1151 peer_id = %peer_id,
1152 num = assignments.len(),
1153 "Processing assignments from a peer",
1154 );
1155 let sanitized_assignments =
1156 self.sanitize_v2_assignments(peer_id, network_sender, assignments).await;
1157
1158 self.process_incoming_assignments(
1159 approval_voting_sender,
1160 network_sender,
1161 runtime_api_sender,
1162 metrics,
1163 peer_id,
1164 sanitized_assignments,
1165 rng,
1166 assignment_criteria,
1167 clock,
1168 session_info_provider,
1169 )
1170 .await;
1171 },
1172 ValidationProtocols::V3(protocol_v3::ApprovalDistributionMessage::Approvals(
1173 approvals,
1174 )) => {
1175 let sanitized_approvals =
1176 self.sanitize_v2_approvals(peer_id, network_sender, approvals).await;
1177 self.process_incoming_approvals(
1178 approval_voting_sender,
1179 network_sender,
1180 runtime_api_sender,
1181 metrics,
1182 peer_id,
1183 sanitized_approvals,
1184 session_info_provider,
1185 )
1186 .await;
1187 },
1188 }
1189 }
1190
1191 async fn handle_peer_view_change<N: overseer::SubsystemSender<NetworkBridgeTxMessage>, R>(
1194 &mut self,
1195 network_sender: &mut N,
1196 metrics: &Metrics,
1197 peer_id: PeerId,
1198 view: View,
1199 rng: &mut R,
1200 ) where
1201 R: CryptoRng + Rng,
1202 {
1203 gum::trace!(target: LOG_TARGET, ?view, "Peer view change");
1204 let finalized_number = view.finalized_number;
1205
1206 let (old_view, protocol_version) =
1207 if let Some(peer_entry) = self.peer_views.get_mut(&peer_id) {
1208 (Some(std::mem::replace(&mut peer_entry.view, view.clone())), peer_entry.version)
1209 } else {
1210 gum::warn!(
1212 target: LOG_TARGET,
1213 ?peer_id,
1214 ?view,
1215 "Peer view change for missing `peer_entry`"
1216 );
1217
1218 (None, ValidationVersion::V3.into())
1219 };
1220
1221 let old_finalized_number = old_view.map(|v| v.finalized_number).unwrap_or(0);
1222
1223 let blocks = &mut self.blocks;
1225 let range = old_finalized_number..=finalized_number;
1230 if !range.is_empty() && !blocks.is_empty() {
1231 self.blocks_by_number
1232 .range(range)
1233 .flat_map(|(_number, hashes)| hashes)
1234 .for_each(|hash| {
1235 if let Some(entry) = blocks.get_mut(hash) {
1236 entry.known_by.remove(&peer_id);
1237 }
1238 });
1239 }
1240
1241 Self::unify_with_peer(
1242 network_sender,
1243 metrics,
1244 &mut self.blocks,
1245 &self.topologies,
1246 self.peer_views.len(),
1247 peer_id,
1248 protocol_version,
1249 view,
1250 rng,
1251 false,
1252 )
1253 .await;
1254 }
1255
1256 async fn handle_block_finalized<N: overseer::SubsystemSender<NetworkBridgeTxMessage>>(
1257 &mut self,
1258 network_sender: &mut N,
1259 metrics: &Metrics,
1260 finalized_number: BlockNumber,
1261 ) {
1262 let split_point = finalized_number.saturating_add(1);
1266 let mut old_blocks = self.blocks_by_number.split_off(&split_point);
1267
1268 std::mem::swap(&mut self.blocks_by_number, &mut old_blocks);
1270
1271 old_blocks.values().flatten().for_each(|relay_block| {
1273 self.recent_outdated_blocks.note_outdated(*relay_block);
1274 if let Some(block_entry) = self.blocks.remove(relay_block) {
1275 self.topologies.dec_session_refs(block_entry.session);
1276 }
1277 });
1278
1279 self.enable_aggression(network_sender, Resend::No, metrics).await;
1282 }
1283
1284 fn accept_duplicates_from_validators(
1289 blocks_by_number: &BTreeMap<BlockNumber, Vec<Hash>>,
1290 topologies: &SessionGridTopologies,
1291 aggression_config: &AggressionConfig,
1292 entry: &BlockEntry,
1293 peer: PeerId,
1294 ) -> bool {
1295 let topology = topologies.get_topology(entry.session);
1296 let min_age = blocks_by_number.iter().next().map(|(num, _)| num);
1297 let max_age = blocks_by_number.iter().rev().next().map(|(num, _)| num);
1298
1299 let (min_age, max_age) = match (min_age, max_age) {
1301 (Some(min), Some(max)) => (*min, *max),
1302 _ => return false,
1303 };
1304
1305 let age = max_age.saturating_sub(min_age);
1306
1307 aggression_config.should_trigger_aggression(age) &&
1308 topology.map(|topology| topology.is_validator(&peer)).unwrap_or(false)
1309 }
1310
1311 async fn import_and_circulate_assignment<A, N, RA, R>(
1312 &mut self,
1313 approval_voting_sender: &mut A,
1314 network_sender: &mut N,
1315 runtime_api_sender: &mut RA,
1316 metrics: &Metrics,
1317 source: MessageSource,
1318 assignment: IndirectAssignmentCertV2,
1319 claimed_candidate_indices: CandidateBitfield,
1320 rng: &mut R,
1321 assignment_criteria: &(impl AssignmentCriteria + ?Sized),
1322 clock: &(impl Clock + ?Sized),
1323 session_info_provider: &mut RuntimeInfo,
1324 ) where
1325 A: overseer::SubsystemSender<ApprovalVotingMessage>,
1326 N: overseer::SubsystemSender<NetworkBridgeTxMessage>,
1327 RA: overseer::SubsystemSender<RuntimeApiMessage>,
1328 R: CryptoRng + Rng,
1329 {
1330 let block_hash = assignment.block_hash;
1331 let validator_index = assignment.validator;
1332
1333 let entry = match self.blocks.get_mut(&block_hash) {
1334 Some(entry) => entry,
1335 None => {
1336 if let Some(peer_id) = source.peer_id() {
1337 gum::trace!(
1338 target: LOG_TARGET,
1339 ?peer_id,
1340 hash = ?block_hash,
1341 ?validator_index,
1342 "Unexpected assignment",
1343 );
1344 if !self.recent_outdated_blocks.is_recent_outdated(&block_hash) {
1345 modify_reputation(
1346 &mut self.reputation,
1347 network_sender,
1348 peer_id,
1349 COST_UNEXPECTED_MESSAGE,
1350 )
1351 .await;
1352 gum::debug!(target: LOG_TARGET, "Received assignment for invalid block");
1353 metrics.on_assignment_recent_outdated();
1354 }
1355 }
1356 metrics.on_assignment_invalid_block();
1357 return;
1358 },
1359 };
1360
1361 let (message_subject, message_kind) = (
1363 MessageSubject(block_hash, claimed_candidate_indices.clone(), validator_index),
1364 MessageKind::Assignment,
1365 );
1366
1367 if let Some(peer_id) = source.peer_id() {
1368 match entry.known_by.entry(peer_id) {
1370 hash_map::Entry::Occupied(mut peer_knowledge) => {
1371 let peer_knowledge = peer_knowledge.get_mut();
1372 if peer_knowledge.contains(&message_subject, message_kind) {
1373 if !peer_knowledge.received.insert(message_subject.clone(), message_kind) {
1375 if !Self::accept_duplicates_from_validators(
1376 &self.blocks_by_number,
1377 &self.topologies,
1378 &self.aggression_config,
1379 entry,
1380 peer_id,
1381 ) {
1382 gum::debug!(
1383 target: LOG_TARGET,
1384 ?peer_id,
1385 ?message_subject,
1386 "Duplicate assignment",
1387 );
1388
1389 modify_reputation(
1390 &mut self.reputation,
1391 network_sender,
1392 peer_id,
1393 COST_DUPLICATE_MESSAGE,
1394 )
1395 .await;
1396 }
1397
1398 metrics.on_assignment_duplicate();
1399 } else {
1400 gum::trace!(
1401 target: LOG_TARGET,
1402 ?peer_id,
1403 hash = ?block_hash,
1404 ?validator_index,
1405 ?message_subject,
1406 "We sent the message to the peer while peer was sending it to us. Known race condition.",
1407 );
1408 }
1409 return;
1410 }
1411 },
1412 hash_map::Entry::Vacant(_) => {
1413 gum::debug!(
1414 target: LOG_TARGET,
1415 ?peer_id,
1416 ?message_subject,
1417 "Assignment from a peer is out of view",
1418 );
1419 modify_reputation(
1420 &mut self.reputation,
1421 network_sender,
1422 peer_id,
1423 COST_UNEXPECTED_MESSAGE,
1424 )
1425 .await;
1426 metrics.on_assignment_out_of_view();
1427 },
1428 }
1429
1430 if entry.knowledge.contains(&message_subject, message_kind) {
1432 modify_reputation(
1433 &mut self.reputation,
1434 network_sender,
1435 peer_id,
1436 BENEFIT_VALID_MESSAGE,
1437 )
1438 .await;
1439 if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) {
1440 gum::trace!(target: LOG_TARGET, ?peer_id, ?message_subject, "Known assignment");
1441 peer_knowledge.received.insert(message_subject, message_kind);
1442 }
1443 metrics.on_assignment_good_known();
1444 return;
1445 }
1446
1447 let result = Self::check_assignment_valid(
1448 assignment_criteria,
1449 &entry,
1450 &assignment,
1451 &claimed_candidate_indices,
1452 session_info_provider,
1453 runtime_api_sender,
1454 )
1455 .await;
1456
1457 match result {
1458 Ok(checked_assignment) => {
1459 let current_tranche = clock.tranche_now(self.slot_duration_millis, entry.slot);
1460 let too_far_in_future =
1461 current_tranche + TICK_TOO_FAR_IN_FUTURE as DelayTranche;
1462
1463 if checked_assignment.tranche() >= too_far_in_future {
1464 gum::debug!(
1465 target: LOG_TARGET,
1466 hash = ?block_hash,
1467 ?peer_id,
1468 "Got an assignment too far in the future",
1469 );
1470 modify_reputation(
1471 &mut self.reputation,
1472 network_sender,
1473 peer_id,
1474 COST_ASSIGNMENT_TOO_FAR_IN_THE_FUTURE,
1475 )
1476 .await;
1477 metrics.on_assignment_far();
1478
1479 return;
1480 }
1481
1482 approval_voting_sender
1483 .send_message(ApprovalVotingMessage::ImportAssignment(
1484 checked_assignment,
1485 None,
1486 ))
1487 .await;
1488 modify_reputation(
1489 &mut self.reputation,
1490 network_sender,
1491 peer_id,
1492 BENEFIT_VALID_MESSAGE_FIRST,
1493 )
1494 .await;
1495 entry.knowledge.insert(message_subject.clone(), message_kind);
1496 if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) {
1497 peer_knowledge.received.insert(message_subject.clone(), message_kind);
1498 }
1499 },
1500 Err(error) => {
1501 gum::info!(
1502 target: LOG_TARGET,
1503 hash = ?block_hash,
1504 ?peer_id,
1505 ?error,
1506 "Got a bad assignment from peer",
1507 );
1508 modify_reputation(
1509 &mut self.reputation,
1510 network_sender,
1511 peer_id,
1512 COST_INVALID_MESSAGE,
1513 )
1514 .await;
1515 metrics.on_assignment_bad();
1516 return;
1517 },
1518 }
1519 } else {
1520 if !entry.knowledge.insert(message_subject.clone(), message_kind) {
1521 gum::warn!(
1523 target: LOG_TARGET,
1524 ?message_subject,
1525 "Importing locally an already known assignment",
1526 );
1527 return;
1528 } else {
1529 gum::debug!(
1530 target: LOG_TARGET,
1531 ?message_subject,
1532 "Importing locally a new assignment",
1533 );
1534 }
1535 }
1536
1537 metrics.on_assignment_imported(&assignment.cert.kind);
1540
1541 let topology = self.topologies.get_topology(entry.session);
1542 let local = source == MessageSource::Local;
1543
1544 let required_routing = topology.map_or(RequiredRouting::PendingTopology, |t| {
1545 t.local_grid_neighbors().required_routing_by_index(validator_index, local)
1546 });
1547 let mut peers = HashSet::new();
1549
1550 let peers_to_route_to = topology
1551 .as_ref()
1552 .map(|t| t.peers_to_route(required_routing))
1553 .unwrap_or_default();
1554
1555 for peer in peers_to_route_to {
1556 if !entry.known_by.contains_key(&peer) {
1557 continue;
1558 }
1559
1560 peers.insert(peer);
1561 }
1562
1563 let peers_to_filter = entry.known_by();
1565
1566 let approval_entry = entry.insert_approval_entry(ApprovalEntry::new(
1567 assignment.clone(),
1568 claimed_candidate_indices.clone(),
1569 ApprovalRouting {
1570 required_routing,
1571 local,
1572 random_routing: Default::default(),
1573 peers_randomly_routed: Default::default(),
1574 },
1575 ));
1576
1577 let assignments = vec![(assignment, claimed_candidate_indices.clone())];
1584 let n_peers_total = self.peer_views.len();
1585 let source_peer = source.peer_id();
1586
1587 for peer in peers_to_filter.into_iter() {
1589 if Some(peer) == source_peer {
1590 continue;
1591 }
1592
1593 if peers.contains(&peer) {
1594 continue;
1595 }
1596
1597 if !topology.map(|topology| topology.is_validator(&peer)).unwrap_or(false) {
1598 continue;
1599 }
1600
1601 let route_random =
1605 approval_entry.routing_info().random_routing.sample(n_peers_total, rng);
1606
1607 if route_random {
1608 approval_entry.routing_info_mut().mark_randomly_sent(peer);
1609 peers.insert(peer);
1610 }
1611
1612 if approval_entry.routing_info().random_routing.is_complete() {
1613 break;
1614 }
1615 }
1616
1617 for peer in peers.iter() {
1619 if let Some(peer_knowledge) = entry.known_by.get_mut(peer) {
1621 peer_knowledge.sent.insert(message_subject.clone(), message_kind);
1622 }
1623 }
1624
1625 if !peers.is_empty() {
1626 gum::trace!(
1627 target: LOG_TARGET,
1628 ?block_hash,
1629 ?claimed_candidate_indices,
1630 local = source.peer_id().is_none(),
1631 num_peers = peers.len(),
1632 "Sending an assignment to peers",
1633 );
1634
1635 let peers = peers
1636 .iter()
1637 .filter_map(|peer_id| {
1638 self.peer_views.get(peer_id).map(|peer_entry| (*peer_id, peer_entry.version))
1639 })
1640 .collect::<Vec<_>>();
1641
1642 send_assignments_batched(network_sender, assignments, &peers).await;
1643 }
1644 }
1645
1646 async fn check_assignment_valid<RA: overseer::SubsystemSender<RuntimeApiMessage>>(
1647 assignment_criteria: &(impl AssignmentCriteria + ?Sized),
1648 entry: &BlockEntry,
1649 assignment: &IndirectAssignmentCertV2,
1650 claimed_candidate_indices: &CandidateBitfield,
1651 runtime_info: &mut RuntimeInfo,
1652 runtime_api_sender: &mut RA,
1653 ) -> Result<CheckedIndirectAssignment, InvalidAssignmentError> {
1654 let ExtendedSessionInfo { ref session_info, .. } = runtime_info
1655 .get_session_info_by_index(runtime_api_sender, assignment.block_hash, entry.session)
1656 .await
1657 .map_err(|err| InvalidAssignmentError::SessionInfoNotFound(err))?;
1658
1659 if claimed_candidate_indices.len() > session_info.n_cores as usize {
1660 return Err(InvalidAssignmentError::OversizedClaimedBitfield);
1661 }
1662
1663 let claimed_cores: Vec<CoreIndex> = claimed_candidate_indices
1664 .iter_ones()
1665 .map(|candidate_index| {
1666 entry.candidates_metadata.get(candidate_index).map(|(_, core, _)| *core).ok_or(
1667 InvalidAssignmentError::ClaimedInvalidCandidateIndex {
1668 claimed_index: candidate_index,
1669 max_index: entry.candidates_metadata.len(),
1670 },
1671 )
1672 })
1673 .collect::<Result<Vec<_>, InvalidAssignmentError>>()?;
1674
1675 let Ok(claimed_cores) = claimed_cores.try_into() else {
1676 return Err(InvalidAssignmentError::NoClaimedCandidates);
1677 };
1678
1679 let backing_groups = claimed_candidate_indices
1680 .iter_ones()
1681 .flat_map(|candidate_index| {
1682 entry.candidates_metadata.get(candidate_index).map(|(_, _, group)| *group)
1683 })
1684 .collect::<Vec<_>>();
1685
1686 assignment_criteria
1687 .check_assignment_cert(
1688 claimed_cores,
1689 assignment.validator,
1690 &polkadot_node_primitives::approval::criteria::Config::from(session_info),
1691 entry.vrf_story.clone(),
1692 &assignment.cert,
1693 backing_groups,
1694 )
1695 .map_err(|err| InvalidAssignmentError::CryptoCheckFailed(err))
1696 .map(|tranche| {
1697 CheckedIndirectAssignment::from_checked(
1698 assignment.clone(),
1699 claimed_candidate_indices.clone(),
1700 tranche,
1701 )
1702 })
1703 }
1704 async fn check_approval_can_be_processed<
1707 N: overseer::SubsystemSender<NetworkBridgeTxMessage>,
1708 >(
1709 network_sender: &mut N,
1710 assignments_knowledge_key: &Vec<(MessageSubject, MessageKind)>,
1711 approval_knowledge_key: &(MessageSubject, MessageKind),
1712 entry: &mut BlockEntry,
1713 blocks_by_number: &BTreeMap<BlockNumber, Vec<Hash>>,
1714 topologies: &SessionGridTopologies,
1715 aggression_config: &AggressionConfig,
1716 reputation: &mut ReputationAggregator,
1717 peer_id: PeerId,
1718 metrics: &Metrics,
1719 ) -> bool {
1720 for message_subject in assignments_knowledge_key {
1721 if !entry.knowledge.contains(&message_subject.0, message_subject.1) {
1722 gum::trace!(
1723 target: LOG_TARGET,
1724 ?peer_id,
1725 ?message_subject,
1726 "Unknown approval assignment",
1727 );
1728 modify_reputation(reputation, network_sender, peer_id, COST_UNEXPECTED_MESSAGE)
1729 .await;
1730 metrics.on_approval_unknown_assignment();
1731 return false;
1732 }
1733 }
1734
1735 match entry.known_by.entry(peer_id) {
1737 hash_map::Entry::Occupied(mut knowledge) => {
1738 let peer_knowledge = knowledge.get_mut();
1739 if peer_knowledge.contains(&approval_knowledge_key.0, approval_knowledge_key.1) {
1740 if !peer_knowledge
1741 .received
1742 .insert(approval_knowledge_key.0.clone(), approval_knowledge_key.1)
1743 {
1744 if !Self::accept_duplicates_from_validators(
1745 blocks_by_number,
1746 topologies,
1747 aggression_config,
1748 entry,
1749 peer_id,
1750 ) {
1751 gum::trace!(
1752 target: LOG_TARGET,
1753 ?peer_id,
1754 ?approval_knowledge_key,
1755 "Duplicate approval",
1756 );
1757 modify_reputation(
1758 reputation,
1759 network_sender,
1760 peer_id,
1761 COST_DUPLICATE_MESSAGE,
1762 )
1763 .await;
1764 }
1765 metrics.on_approval_duplicate();
1766 }
1767 return false;
1768 }
1769 },
1770 hash_map::Entry::Vacant(_) => {
1771 gum::debug!(
1772 target: LOG_TARGET,
1773 ?peer_id,
1774 ?approval_knowledge_key,
1775 "Approval from a peer is out of view",
1776 );
1777 modify_reputation(reputation, network_sender, peer_id, COST_UNEXPECTED_MESSAGE)
1778 .await;
1779 metrics.on_approval_out_of_view();
1780 },
1781 }
1782
1783 if entry.knowledge.contains(&approval_knowledge_key.0, approval_knowledge_key.1) {
1784 if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) {
1785 peer_knowledge
1786 .received
1787 .insert(approval_knowledge_key.0.clone(), approval_knowledge_key.1);
1788 }
1789
1790 gum::trace!(target: LOG_TARGET, ?peer_id, ?approval_knowledge_key, "Known approval");
1792 metrics.on_approval_good_known();
1793 modify_reputation(reputation, network_sender, peer_id, BENEFIT_VALID_MESSAGE).await;
1794 false
1795 } else {
1796 true
1797 }
1798 }
1799
1800 async fn import_and_circulate_approval<
1801 N: overseer::SubsystemSender<NetworkBridgeTxMessage>,
1802 A: overseer::SubsystemSender<ApprovalVotingMessage>,
1803 RA: overseer::SubsystemSender<RuntimeApiMessage>,
1804 >(
1805 &mut self,
1806 approval_voting_sender: &mut A,
1807 network_sender: &mut N,
1808 runtime_api_sender: &mut RA,
1809 metrics: &Metrics,
1810 source: MessageSource,
1811 vote: IndirectSignedApprovalVoteV2,
1812 session_info_provider: &mut RuntimeInfo,
1813 ) {
1814 let block_hash = vote.block_hash;
1815 let validator_index = vote.validator;
1816 let candidate_indices = &vote.candidate_indices;
1817 let entry = match self.blocks.get_mut(&block_hash) {
1818 Some(entry) if entry.contains_candidates(&vote.candidate_indices) => entry,
1819 _ => {
1820 if let Some(peer_id) = source.peer_id() {
1821 if !self.recent_outdated_blocks.is_recent_outdated(&block_hash) {
1822 gum::debug!(
1823 target: LOG_TARGET,
1824 ?peer_id,
1825 ?block_hash,
1826 ?validator_index,
1827 ?candidate_indices,
1828 "Approval from a peer is out of view",
1829 );
1830 modify_reputation(
1831 &mut self.reputation,
1832 network_sender,
1833 peer_id,
1834 COST_UNEXPECTED_MESSAGE,
1835 )
1836 .await;
1837 metrics.on_approval_invalid_block();
1838 } else {
1839 metrics.on_approval_recent_outdated();
1840 }
1841 }
1842 return;
1843 },
1844 };
1845
1846 let assignments_knowledge_keys = PeerKnowledge::generate_assignments_keys(&vote);
1848 let approval_knwowledge_key = PeerKnowledge::generate_approval_key(&vote);
1849
1850 if let Some(peer_id) = source.peer_id() {
1851 if !Self::check_approval_can_be_processed(
1852 network_sender,
1853 &assignments_knowledge_keys,
1854 &approval_knwowledge_key,
1855 entry,
1856 &self.blocks_by_number,
1857 &self.topologies,
1858 &self.aggression_config,
1859 &mut self.reputation,
1860 peer_id,
1861 metrics,
1862 )
1863 .await
1864 {
1865 return;
1866 }
1867
1868 let result =
1869 Self::check_vote_valid(&vote, &entry, session_info_provider, runtime_api_sender)
1870 .await;
1871
1872 match result {
1873 Ok(vote) => {
1874 approval_voting_sender
1875 .send_message(ApprovalVotingMessage::ImportApproval(vote, None))
1876 .await;
1877
1878 modify_reputation(
1879 &mut self.reputation,
1880 network_sender,
1881 peer_id,
1882 BENEFIT_VALID_MESSAGE_FIRST,
1883 )
1884 .await;
1885
1886 entry
1887 .knowledge
1888 .insert(approval_knwowledge_key.0.clone(), approval_knwowledge_key.1);
1889 if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) {
1890 peer_knowledge
1891 .received
1892 .insert(approval_knwowledge_key.0.clone(), approval_knwowledge_key.1);
1893 }
1894 },
1895 Err(err) => {
1896 modify_reputation(
1897 &mut self.reputation,
1898 network_sender,
1899 peer_id,
1900 COST_INVALID_MESSAGE,
1901 )
1902 .await;
1903
1904 gum::info!(
1905 target: LOG_TARGET,
1906 ?peer_id,
1907 ?err,
1908 "Got a bad approval from peer",
1909 );
1910 metrics.on_approval_bad();
1911 return;
1912 },
1913 }
1914 } else {
1915 if !entry
1916 .knowledge
1917 .insert(approval_knwowledge_key.0.clone(), approval_knwowledge_key.1)
1918 {
1919 gum::warn!(
1921 target: LOG_TARGET,
1922 "Importing locally an already known approval",
1923 );
1924 return;
1925 } else {
1926 gum::debug!(
1927 target: LOG_TARGET,
1928 "Importing locally a new approval",
1929 );
1930 }
1931 }
1932
1933 let (required_routing, peers_randomly_routed_to) = match entry.note_approval(vote.clone()) {
1934 Ok(required_routing) => required_routing,
1935 Err(err) => {
1936 gum::warn!(
1937 target: LOG_TARGET,
1938 hash = ?block_hash,
1939 validator_index = ?vote.validator,
1940 candidate_bitfield = ?vote.candidate_indices,
1941 ?err,
1942 "Possible bug: Vote import failed",
1943 );
1944 metrics.on_approval_bug();
1945 return;
1946 },
1947 };
1948
1949 metrics.on_approval_imported();
1952
1953 let topology = self.topologies.get_topology(entry.session);
1956 let source_peer = source.peer_id();
1957
1958 let peer_filter = move |peer| {
1959 if Some(peer) == source_peer.as_ref() {
1960 return false;
1961 }
1962
1963 let in_topology = topology
1973 .map_or(false, |t| t.local_grid_neighbors().route_to_peer(required_routing, peer));
1974 in_topology || peers_randomly_routed_to.contains(peer)
1975 };
1976
1977 let peers = entry
1978 .known_by
1979 .iter()
1980 .filter(|(p, _)| peer_filter(p))
1981 .filter_map(|(p, _)| self.peer_views.get(p).map(|entry| (*p, entry.version)))
1982 .collect::<Vec<_>>();
1983
1984 for peer in peers.iter() {
1986 if let Some(entry) = entry.known_by.get_mut(&peer.0) {
1988 entry.sent.insert(approval_knwowledge_key.0.clone(), approval_knwowledge_key.1);
1989 }
1990 }
1991
1992 if !peers.is_empty() {
1993 let approvals = vec![vote];
1994 gum::trace!(
1995 target: LOG_TARGET,
1996 ?block_hash,
1997 local = source.peer_id().is_none(),
1998 num_peers = peers.len(),
1999 "Sending an approval to peers",
2000 );
2001 send_approvals_batched(network_sender, approvals, &peers).await;
2002 }
2003 }
2004
2005 async fn check_vote_valid<RA: overseer::SubsystemSender<RuntimeApiMessage>>(
2007 vote: &IndirectSignedApprovalVoteV2,
2008 entry: &BlockEntry,
2009 runtime_info: &mut RuntimeInfo,
2010 runtime_api_sender: &mut RA,
2011 ) -> Result<CheckedIndirectSignedApprovalVote, InvalidVoteError> {
2012 if vote.candidate_indices.len() > entry.candidates_metadata.len() {
2013 return Err(InvalidVoteError::CandidateIndexOutOfBounds);
2014 }
2015
2016 let candidate_hashes = vote
2017 .candidate_indices
2018 .iter_ones()
2019 .flat_map(|candidate_index| {
2020 entry
2021 .candidates_metadata
2022 .get(candidate_index)
2023 .map(|(candidate_hash, _, _)| *candidate_hash)
2024 })
2025 .collect::<Vec<_>>();
2026
2027 let ExtendedSessionInfo { ref session_info, ref approval_voting_params, .. } = runtime_info
2028 .get_session_info_by_index(runtime_api_sender, vote.block_hash, entry.session)
2029 .await
2030 .map_err(|err| InvalidVoteError::SessionInfoNotFound(err))?;
2031
2032 if candidate_hashes.len() > approval_voting_params.max_approval_coalesce_count as usize {
2035 return Err(InvalidVoteError::TooManyCandidates);
2036 }
2037
2038 let pubkey = session_info
2039 .validators
2040 .get(vote.validator)
2041 .ok_or(InvalidVoteError::ValidatorIndexOutOfBounds)?;
2042 let candidate_hashes: BoundedVec<CandidateHash, ConstU32<{ MAX_COALESCE_APPROVALS }>> =
2043 candidate_hashes.try_into().map_err(|_| InvalidVoteError::TooManyCandidates)?;
2044 let first_candidate =
2045 *candidate_hashes.first().ok_or(InvalidVoteError::CandidateHashNotFound)?;
2046 DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates(
2047 candidate_hashes,
2048 ))
2049 .check_signature(&pubkey, first_candidate, entry.session, &vote.signature)
2050 .map_err(|_| InvalidVoteError::InvalidSignature)
2051 .map(|_| CheckedIndirectSignedApprovalVote::from_checked(vote.clone()))
2052 }
2053
2054 fn get_approval_signatures(
2056 &mut self,
2057 indices: HashSet<(Hash, CandidateIndex)>,
2058 ) -> HashMap<ValidatorIndex, (Hash, Vec<CandidateIndex>, ValidatorSignature)> {
2059 let mut all_sigs = HashMap::new();
2060 for (hash, index) in indices {
2061 let block_entry = match self.blocks.get(&hash) {
2062 None => {
2063 gum::debug!(
2064 target: LOG_TARGET,
2065 ?hash,
2066 "`get_approval_signatures`: could not find block entry for given hash!"
2067 );
2068 continue;
2069 },
2070 Some(e) => e,
2071 };
2072
2073 let sigs = block_entry.approval_votes(index).into_iter().map(|approval| {
2074 (
2075 approval.validator,
2076 (
2077 hash,
2078 approval
2079 .candidate_indices
2080 .iter_ones()
2081 .map(|val| val as CandidateIndex)
2082 .collect_vec(),
2083 approval.signature,
2084 ),
2085 )
2086 });
2087 all_sigs.extend(sigs);
2088 }
2089 all_sigs
2090 }
2091
2092 async fn unify_with_peer(
2093 sender: &mut impl overseer::SubsystemSender<NetworkBridgeTxMessage>,
2094 metrics: &Metrics,
2095 entries: &mut HashMap<Hash, BlockEntry>,
2096 topologies: &SessionGridTopologies,
2097 total_peers: usize,
2098 peer_id: PeerId,
2099 protocol_version: ProtocolVersion,
2100 view: View,
2101 rng: &mut (impl CryptoRng + Rng),
2102 retry_known_blocks: bool,
2103 ) {
2104 metrics.on_unify_with_peer();
2105 let _timer = metrics.time_unify_with_peer();
2106
2107 let mut assignments_to_send = Vec::new();
2108 let mut approvals_to_send = Vec::new();
2109
2110 let view_finalized_number = view.finalized_number;
2111 for head in view.into_iter() {
2112 let mut block = head;
2113
2114 loop {
2116 let entry = match entries.get_mut(&block) {
2117 Some(entry) if entry.number > view_finalized_number => entry,
2118 _ => break,
2119 };
2120
2121 if entry.known_by.contains_key(&peer_id) && !retry_known_blocks {
2127 break;
2128 }
2129
2130 let peer_knowledge = entry.known_by.entry(peer_id).or_default();
2131 let topology = topologies.get_topology(entry.session);
2132
2133 for approval_entry in entry.approval_entries.values_mut() {
2136 {
2139 let required_routing = approval_entry.routing_info().required_routing;
2140 let routing_info = &mut approval_entry.routing_info_mut();
2141 let rng = &mut *rng;
2142 let mut peer_filter = move |peer_id| {
2143 let in_topology = topology.as_ref().map_or(false, |t| {
2144 t.local_grid_neighbors().route_to_peer(required_routing, peer_id)
2145 });
2146 in_topology || {
2147 if !topology
2148 .map(|topology| topology.is_validator(peer_id))
2149 .unwrap_or(false)
2150 {
2151 return false;
2152 }
2153
2154 let route_random =
2155 routing_info.random_routing.sample(total_peers, rng);
2156 if route_random {
2157 routing_info.mark_randomly_sent(*peer_id);
2158 }
2159
2160 route_random
2161 }
2162 };
2163
2164 if !peer_filter(&peer_id) {
2165 continue;
2166 }
2167 }
2168
2169 let assignment_message = approval_entry.assignment();
2170 let approval_messages = approval_entry.approvals();
2171 let (assignment_knowledge, message_kind) =
2172 approval_entry.create_assignment_knowledge(block);
2173
2174 if !peer_knowledge.contains(&assignment_knowledge, message_kind) {
2177 peer_knowledge.sent.insert(assignment_knowledge, message_kind);
2178 assignments_to_send.push(assignment_message);
2179 }
2180
2181 for approval_message in approval_messages {
2183 let approval_knowledge =
2184 PeerKnowledge::generate_approval_key(&approval_message);
2185
2186 if !peer_knowledge.contains(&approval_knowledge.0, approval_knowledge.1) {
2187 approvals_to_send.push(approval_message);
2188 peer_knowledge.sent.insert(approval_knowledge.0, approval_knowledge.1);
2189 }
2190 }
2191 }
2192
2193 block = entry.parent_hash;
2194 }
2195 }
2196
2197 if !assignments_to_send.is_empty() {
2198 gum::trace!(
2199 target: LOG_TARGET,
2200 ?peer_id,
2201 ?protocol_version,
2202 num = assignments_to_send.len(),
2203 "Sending assignments to unified peer",
2204 );
2205
2206 send_assignments_batched(
2207 sender,
2208 assignments_to_send,
2209 &vec![(peer_id, protocol_version)],
2210 )
2211 .await;
2212 }
2213
2214 if !approvals_to_send.is_empty() {
2215 gum::trace!(
2216 target: LOG_TARGET,
2217 ?peer_id,
2218 ?protocol_version,
2219 num = approvals_to_send.len(),
2220 "Sending approvals to unified peer",
2221 );
2222
2223 send_approvals_batched(sender, approvals_to_send, &vec![(peer_id, protocol_version)])
2224 .await;
2225 }
2226 }
2227
2228 async fn enable_aggression<N: overseer::SubsystemSender<NetworkBridgeTxMessage>>(
2239 &mut self,
2240 network_sender: &mut N,
2241 resend: Resend,
2242 metrics: &Metrics,
2243 ) {
2244 let config = self.aggression_config.clone();
2245 let min_age = self.blocks_by_number.iter().next().map(|(num, _)| num);
2246 let max_age = self.blocks_by_number.iter().rev().next().map(|(num, _)| num);
2247
2248 let (min_age, max_age) = match (min_age, max_age) {
2250 (Some(min), Some(max)) => (*min, *max),
2251 _ => return, };
2253
2254 let age = max_age.saturating_sub(min_age);
2255
2256 if !self.aggression_config.should_trigger_aggression(age) {
2258 gum::trace!(
2259 target: LOG_TARGET,
2260 approval_checking_lag = self.approval_checking_lag,
2261 age,
2262 "Aggression not enabled",
2263 );
2264 return;
2265 }
2266 gum::debug!(target: LOG_TARGET, min_age, max_age, "Aggression enabled",);
2267
2268 adjust_required_routing_and_propagate(
2269 network_sender,
2270 &mut self.blocks,
2271 &self.topologies,
2272 |block_entry| {
2273 let block_age = max_age - block_entry.number;
2274 let diff_from_min_age = block_entry.number - min_age;
2278
2279 let blocks_since_last_sent = block_entry
2284 .last_resent_at_block_number
2285 .map(|last_resent_at_block_number| max_age - last_resent_at_block_number);
2286
2287 let can_resend_at_this_age = blocks_since_last_sent
2288 .zip(config.resend_unfinalized_period)
2289 .map(|(blocks_since_last_sent, unfinalized_period)| {
2290 blocks_since_last_sent >= unfinalized_period * 2
2291 })
2292 .unwrap_or(true);
2293
2294 if resend == Resend::Yes &&
2295 config.resend_unfinalized_period.as_ref().map_or(false, |p| {
2296 block_age > 0 &&
2297 block_age % p == 0 && diff_from_min_age == 0 &&
2298 can_resend_at_this_age
2299 }) {
2300 for (_, knowledge) in block_entry.known_by.iter_mut() {
2302 knowledge.sent = Knowledge::default();
2303 }
2304 block_entry.last_resent_at_block_number = Some(max_age);
2305 gum::debug!(
2306 target: LOG_TARGET,
2307 block_number = ?block_entry.number,
2308 ?max_age,
2309 "Aggression enabled with resend for block",
2310 );
2311 true
2312 } else {
2313 false
2314 }
2315 },
2316 |required_routing, _, _| *required_routing,
2317 &self.peer_views,
2318 )
2319 .await;
2320
2321 adjust_required_routing_and_propagate(
2322 network_sender,
2323 &mut self.blocks,
2324 &self.topologies,
2325 |block_entry| {
2326 block_entry.number == min_age
2332 },
2333 |required_routing, local, _| {
2334 if *required_routing == RequiredRouting::PendingTopology {
2336 gum::debug!(
2337 target: LOG_TARGET,
2338 lag = ?self.approval_checking_lag,
2339 "Encountered old block pending gossip topology",
2340 );
2341 return *required_routing;
2342 }
2343
2344 let mut new_required_routing = *required_routing;
2345
2346 if config.l1_threshold.as_ref().map_or(false, |t| &age >= t) {
2347 if local && new_required_routing != RequiredRouting::All {
2349 metrics.on_aggression_l1();
2350 new_required_routing = RequiredRouting::All;
2351 }
2352 }
2353
2354 if config.l2_threshold.as_ref().map_or(false, |t| &age >= t) {
2355 if !local && new_required_routing != RequiredRouting::GridXY {
2357 metrics.on_aggression_l2();
2358 new_required_routing = RequiredRouting::GridXY;
2359 }
2360 }
2361 new_required_routing
2362 },
2363 &self.peer_views,
2364 )
2365 .await;
2366 }
2367
2368 async fn sanitize_v2_assignments(
2371 &mut self,
2372 peer_id: PeerId,
2373 sender: &mut impl overseer::SubsystemSender<NetworkBridgeTxMessage>,
2374 assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)>,
2375 ) -> Vec<(IndirectAssignmentCertV2, CandidateBitfield)> {
2376 let mut sanitized_assignments = Vec::new();
2377 for (cert, candidate_bitfield) in assignments.into_iter() {
2378 let cert_bitfield_bits = match &cert.cert.kind {
2379 AssignmentCertKindV2::RelayVRFDelay { core_index } => core_index.0 as usize + 1,
2380 AssignmentCertKindV2::RelayVRFModuloCompact { core_bitfield } => {
2381 core_bitfield.len()
2382 },
2383 };
2384
2385 let candidate_bitfield_bits = candidate_bitfield.len();
2386
2387 let msb = candidate_bitfield_bits - 1;
2389
2390 if cert_bitfield_bits > MAX_BITFIELD_SIZE
2392 || candidate_bitfield_bits > MAX_BITFIELD_SIZE
2393 || !candidate_bitfield.bit_at(msb.as_bit_index())
2395 {
2396 modify_reputation(&mut self.reputation, sender, peer_id, COST_OVERSIZED_BITFIELD)
2398 .await;
2399 for candidate_index in candidate_bitfield.iter_ones() {
2400 gum::debug!(target: LOG_TARGET, block_hash = ?cert.block_hash, ?candidate_index, validator_index = ?cert.validator, "Bad assignment v2, oversized bitfield");
2401 }
2402 } else {
2403 sanitized_assignments.push((cert, candidate_bitfield))
2404 }
2405 }
2406
2407 sanitized_assignments
2408 }
2409
2410 async fn sanitize_v2_approvals(
2412 &mut self,
2413 peer_id: PeerId,
2414 sender: &mut impl overseer::SubsystemSender<NetworkBridgeTxMessage>,
2415 approval: Vec<IndirectSignedApprovalVoteV2>,
2416 ) -> Vec<IndirectSignedApprovalVoteV2> {
2417 let mut sanitized_approvals = Vec::new();
2418 for approval in approval.into_iter() {
2419 let has_no_approved_candidates = approval.candidate_indices.first_one().is_none();
2420 if approval.candidate_indices.len() as usize > MAX_BITFIELD_SIZE ||
2421 has_no_approved_candidates
2422 {
2423 modify_reputation(
2425 &mut self.reputation,
2426 sender,
2427 peer_id,
2428 if has_no_approved_candidates {
2429 COST_INVALID_MESSAGE
2430 } else {
2431 COST_OVERSIZED_BITFIELD
2432 },
2433 )
2434 .await;
2435 gum::debug!(
2436 target: LOG_TARGET,
2437 block_hash = ?approval.block_hash,
2438 candidate_indices_len = ?approval.candidate_indices.len(),
2439 "Bad approval v2, invalid candidate indices size"
2440 );
2441 } else {
2442 sanitized_approvals.push(approval)
2443 }
2444 }
2445
2446 sanitized_approvals
2447 }
2448}
2449
2450#[overseer::contextbounds(ApprovalDistribution, prefix = self::overseer)]
2463async fn adjust_required_routing_and_propagate<
2464 N: overseer::SubsystemSender<NetworkBridgeTxMessage>,
2465 BlockFilter,
2466 RoutingModifier,
2467>(
2468 network_sender: &mut N,
2469 blocks: &mut HashMap<Hash, BlockEntry>,
2470 topologies: &SessionGridTopologies,
2471 block_filter: BlockFilter,
2472 routing_modifier: RoutingModifier,
2473 peer_views: &HashMap<PeerId, PeerEntry>,
2474) where
2475 BlockFilter: Fn(&mut BlockEntry) -> bool,
2476 RoutingModifier: Fn(&RequiredRouting, bool, &ValidatorIndex) -> RequiredRouting,
2477{
2478 let mut peer_assignments = HashMap::new();
2479 let mut peer_approvals = HashMap::new();
2480
2481 for (block_hash, block_entry) in blocks {
2484 if !block_filter(block_entry) {
2485 continue;
2486 }
2487
2488 let topology = match topologies.get_topology(block_entry.session) {
2489 Some(t) => t,
2490 None => continue,
2491 };
2492
2493 for approval_entry in block_entry.approval_entries.values_mut() {
2496 let new_required_routing = routing_modifier(
2497 &approval_entry.routing_info().required_routing,
2498 approval_entry.routing_info().local,
2499 &approval_entry.validator_index(),
2500 );
2501
2502 approval_entry.update_required_routing(new_required_routing);
2503
2504 if approval_entry.routing_info().required_routing.is_empty() {
2505 continue;
2506 }
2507
2508 let assignment_message = approval_entry.assignment();
2509 let approval_messages = approval_entry.approvals();
2510 let (assignment_knowledge, message_kind) =
2511 approval_entry.create_assignment_knowledge(*block_hash);
2512
2513 for (peer, peer_knowledge) in &mut block_entry.known_by {
2514 if !topology
2515 .local_grid_neighbors()
2516 .route_to_peer(approval_entry.routing_info().required_routing, peer)
2517 {
2518 continue;
2519 }
2520
2521 if !peer_knowledge.contains(&assignment_knowledge, message_kind) {
2523 peer_knowledge.sent.insert(assignment_knowledge.clone(), message_kind);
2524 peer_assignments
2525 .entry(*peer)
2526 .or_insert_with(Vec::new)
2527 .push(assignment_message.clone());
2528 }
2529
2530 for approval_message in &approval_messages {
2532 let approval_knowledge = PeerKnowledge::generate_approval_key(approval_message);
2533
2534 if !peer_knowledge.contains(&approval_knowledge.0, approval_knowledge.1) {
2535 peer_knowledge.sent.insert(approval_knowledge.0, approval_knowledge.1);
2536 peer_approvals
2537 .entry(*peer)
2538 .or_insert_with(Vec::new)
2539 .push(approval_message.clone());
2540 }
2541 }
2542 }
2543 }
2544 }
2545
2546 for (peer, assignments_packet) in peer_assignments {
2548 if let Some(peer_view) = peer_views.get(&peer) {
2549 send_assignments_batched(
2550 network_sender,
2551 assignments_packet,
2552 &vec![(peer, peer_view.version)],
2553 )
2554 .await;
2555 } else {
2556 gum::warn!(target: LOG_TARGET, ?peer, "Unknown protocol version for peer",);
2558 }
2559 }
2560
2561 for (peer, approvals_packet) in peer_approvals {
2562 if let Some(peer_view) = peer_views.get(&peer) {
2563 send_approvals_batched(
2564 network_sender,
2565 approvals_packet,
2566 &vec![(peer, peer_view.version)],
2567 )
2568 .await;
2569 } else {
2570 gum::warn!(target: LOG_TARGET, ?peer, "Unknown protocol version for peer",);
2572 }
2573 }
2574}
2575
2576async fn modify_reputation(
2578 reputation: &mut ReputationAggregator,
2579 sender: &mut impl overseer::SubsystemSender<NetworkBridgeTxMessage>,
2580 peer_id: PeerId,
2581 rep: Rep,
2582) {
2583 gum::trace!(
2584 target: LOG_TARGET,
2585 reputation = ?rep,
2586 ?peer_id,
2587 "Reputation change for peer",
2588 );
2589 reputation.modify(sender, peer_id, rep).await;
2590}
2591
2592#[overseer::contextbounds(ApprovalDistribution, prefix = self::overseer)]
2593impl ApprovalDistribution {
2594 pub fn new(
2596 metrics: Metrics,
2597 slot_duration_millis: u64,
2598 assignment_criteria: Arc<dyn AssignmentCriteria + Send + Sync>,
2599 ) -> Self {
2600 Self::new_with_clock(
2601 metrics,
2602 slot_duration_millis,
2603 Arc::new(SystemClock),
2604 assignment_criteria,
2605 )
2606 }
2607
2608 pub fn new_with_clock(
2610 metrics: Metrics,
2611 slot_duration_millis: u64,
2612 clock: Arc<dyn Clock + Send + Sync>,
2613 assignment_criteria: Arc<dyn AssignmentCriteria + Send + Sync>,
2614 ) -> Self {
2615 Self { metrics, slot_duration_millis, clock, assignment_criteria }
2616 }
2617
2618 async fn run<Context>(self, ctx: Context) {
2619 let mut state =
2620 State { slot_duration_millis: self.slot_duration_millis, ..Default::default() };
2621 let mut rng = rand::rngs::StdRng::from_entropy();
2624 let mut session_info_provider = RuntimeInfo::new_with_config(RuntimeInfoConfig {
2625 keystore: None,
2626 session_cache_lru_size: DISPUTE_WINDOW.get(),
2627 });
2628
2629 self.run_inner(
2630 ctx,
2631 &mut state,
2632 REPUTATION_CHANGE_INTERVAL,
2633 &mut rng,
2634 &mut session_info_provider,
2635 )
2636 .await
2637 }
2638
2639 async fn run_inner<Context>(
2641 self,
2642 mut ctx: Context,
2643 state: &mut State,
2644 reputation_interval: Duration,
2645 rng: &mut (impl CryptoRng + Rng),
2646 session_info_provider: &mut RuntimeInfo,
2647 ) {
2648 let new_reputation_delay = || futures_timer::Delay::new(reputation_interval).fuse();
2649 let mut reputation_delay = new_reputation_delay();
2650 let mut approval_voting_sender = ctx.sender().clone();
2651 let mut network_sender = ctx.sender().clone();
2652 let mut runtime_api_sender = ctx.sender().clone();
2653
2654 loop {
2655 select! {
2656 _ = reputation_delay => {
2657 state.reputation.send(ctx.sender()).await;
2658 reputation_delay = new_reputation_delay();
2659 },
2660 message = ctx.recv().fuse() => {
2661 let message = match message {
2662 Ok(message) => message,
2663 Err(e) => {
2664 gum::debug!(target: LOG_TARGET, err = ?e, "Failed to receive a message from Overseer, exiting");
2665 return
2666 },
2667 };
2668
2669 if self.handle_from_orchestra(message, &mut approval_voting_sender, &mut network_sender, &mut runtime_api_sender, state, rng, session_info_provider).await {
2670 return;
2671 }
2672
2673 },
2674 }
2675 }
2676 }
2677
2678 pub async fn handle_from_orchestra<
2682 N: overseer::SubsystemSender<NetworkBridgeTxMessage>,
2683 A: overseer::SubsystemSender<ApprovalVotingMessage>,
2684 RA: overseer::SubsystemSender<RuntimeApiMessage>,
2685 >(
2686 &self,
2687 message: FromOrchestra<ApprovalDistributionMessage>,
2688 approval_voting_sender: &mut A,
2689 network_sender: &mut N,
2690 runtime_api_sender: &mut RA,
2691 state: &mut State,
2692 rng: &mut (impl CryptoRng + Rng),
2693 session_info_provider: &mut RuntimeInfo,
2694 ) -> bool {
2695 match message {
2696 FromOrchestra::Communication { msg } => {
2697 Self::handle_incoming(
2698 approval_voting_sender,
2699 network_sender,
2700 runtime_api_sender,
2701 state,
2702 msg,
2703 &self.metrics,
2704 rng,
2705 self.assignment_criteria.as_ref(),
2706 self.clock.as_ref(),
2707 session_info_provider,
2708 )
2709 .await
2710 },
2711 FromOrchestra::Signal(OverseerSignal::ActiveLeaves(_update)) => {
2712 gum::trace!(target: LOG_TARGET, "active leaves signal (ignored)");
2713 },
2718 FromOrchestra::Signal(OverseerSignal::BlockFinalized(_hash, number)) => {
2719 gum::trace!(target: LOG_TARGET, number = %number, "finalized signal");
2720 state.handle_block_finalized(network_sender, &self.metrics, number).await;
2721 },
2722 FromOrchestra::Signal(OverseerSignal::Conclude) => return true,
2723 }
2724 false
2725 }
2726
2727 async fn handle_incoming<
2728 N: overseer::SubsystemSender<NetworkBridgeTxMessage>,
2729 A: overseer::SubsystemSender<ApprovalVotingMessage>,
2730 RA: overseer::SubsystemSender<RuntimeApiMessage>,
2731 >(
2732 approval_voting_sender: &mut A,
2733 network_sender: &mut N,
2734 runtime_api_sender: &mut RA,
2735 state: &mut State,
2736 msg: ApprovalDistributionMessage,
2737 metrics: &Metrics,
2738 rng: &mut (impl CryptoRng + Rng),
2739 assignment_criteria: &(impl AssignmentCriteria + ?Sized),
2740 clock: &(impl Clock + ?Sized),
2741 session_info_provider: &mut RuntimeInfo,
2742 ) {
2743 match msg {
2744 ApprovalDistributionMessage::NetworkBridgeUpdate(event) => {
2745 state
2746 .handle_network_msg(
2747 approval_voting_sender,
2748 network_sender,
2749 runtime_api_sender,
2750 metrics,
2751 event,
2752 rng,
2753 assignment_criteria,
2754 clock,
2755 session_info_provider,
2756 )
2757 .await;
2758 },
2759 ApprovalDistributionMessage::NewBlocks(metas) => {
2760 state
2761 .handle_new_blocks(
2762 approval_voting_sender,
2763 network_sender,
2764 runtime_api_sender,
2765 metrics,
2766 metas,
2767 rng,
2768 assignment_criteria,
2769 clock,
2770 session_info_provider,
2771 )
2772 .await;
2773 },
2774 ApprovalDistributionMessage::DistributeAssignment(cert, candidate_indices) => {
2775 gum::debug!(
2776 target: LOG_TARGET,
2777 ?candidate_indices,
2778 block_hash = ?cert.block_hash,
2779 assignment_kind = ?cert.cert.kind,
2780 "Distributing our assignment on candidates",
2781 );
2782
2783 state
2784 .import_and_circulate_assignment(
2785 approval_voting_sender,
2786 network_sender,
2787 runtime_api_sender,
2788 &metrics,
2789 MessageSource::Local,
2790 cert,
2791 candidate_indices,
2792 rng,
2793 assignment_criteria,
2794 clock,
2795 session_info_provider,
2796 )
2797 .await;
2798 },
2799 ApprovalDistributionMessage::DistributeApproval(vote) => {
2800 gum::debug!(
2801 target: LOG_TARGET,
2802 "Distributing our approval vote on candidate (block={}, index={:?})",
2803 vote.block_hash,
2804 vote.candidate_indices,
2805 );
2806
2807 state
2808 .import_and_circulate_approval(
2809 approval_voting_sender,
2810 network_sender,
2811 runtime_api_sender,
2812 metrics,
2813 MessageSource::Local,
2814 vote,
2815 session_info_provider,
2816 )
2817 .await;
2818 },
2819 ApprovalDistributionMessage::GetApprovalSignatures(indices, tx) => {
2820 let sigs = state.get_approval_signatures(indices);
2821 if let Err(_) = tx.send(sigs) {
2822 gum::debug!(
2823 target: LOG_TARGET,
2824 "Sending back approval signatures failed, oneshot got closed"
2825 );
2826 }
2827 },
2828 ApprovalDistributionMessage::ApprovalCheckingLagUpdate(lag) => {
2829 gum::debug!(target: LOG_TARGET, lag, "Received `ApprovalCheckingLagUpdate`");
2830 state.approval_checking_lag = lag;
2831 },
2832 }
2833 }
2834}
2835
2836#[overseer::subsystem(ApprovalDistribution, error=SubsystemError, prefix=self::overseer)]
2837impl<Context> ApprovalDistribution {
2838 fn start(self, ctx: Context) -> SpawnedSubsystem {
2839 let future = self.run(ctx).map(|_| Ok(())).boxed();
2840
2841 SpawnedSubsystem { name: "approval-distribution-subsystem", future }
2842 }
2843}
2844
2845const fn ensure_size_not_zero(size: usize) -> usize {
2847 if 0 == size {
2848 panic!("Batch size must be at least 1 (MAX_NOTIFICATION_SIZE constant is too low)",);
2849 }
2850
2851 size
2852}
2853
2854pub const MAX_ASSIGNMENT_BATCH_SIZE: usize = ensure_size_not_zero(
2859 MAX_NOTIFICATION_SIZE as usize /
2860 std::mem::size_of::<(IndirectAssignmentCertV2, CandidateIndex)>() /
2861 3,
2862);
2863
2864pub const MAX_APPROVAL_BATCH_SIZE: usize = ensure_size_not_zero(
2866 MAX_NOTIFICATION_SIZE as usize / std::mem::size_of::<IndirectSignedApprovalVoteV2>() / 3,
2867);
2868
2869async fn send_assignments_batched_inner(
2871 sender: &mut impl overseer::SubsystemSender<NetworkBridgeTxMessage>,
2872 batch: impl IntoIterator<Item = (IndirectAssignmentCertV2, CandidateBitfield)>,
2873 peers: Vec<PeerId>,
2874 _peer_version: ValidationVersion,
2875) {
2876 sender
2877 .send_message(NetworkBridgeTxMessage::SendValidationMessage(
2878 peers,
2879 ValidationProtocols::V3(protocol_v3::ValidationProtocol::ApprovalDistribution(
2880 protocol_v3::ApprovalDistributionMessage::Assignments(batch.into_iter().collect()),
2881 )),
2882 ))
2883 .await;
2884}
2885
2886pub(crate) async fn send_assignments_batched(
2892 network_sender: &mut impl overseer::SubsystemSender<NetworkBridgeTxMessage>,
2893 v2_assignments: impl IntoIterator<Item = (IndirectAssignmentCertV2, CandidateBitfield)> + Clone,
2894 peers: &[(PeerId, ProtocolVersion)],
2895) {
2896 let v3_peers = filter_by_peer_version(peers, ValidationVersion::V3.into());
2897
2898 if !v3_peers.is_empty() {
2899 let mut v3 = v2_assignments.into_iter().peekable();
2900
2901 while v3.peek().is_some() {
2902 let batch = v3.by_ref().take(MAX_ASSIGNMENT_BATCH_SIZE).collect::<Vec<_>>();
2903 send_assignments_batched_inner(
2904 network_sender,
2905 batch,
2906 v3_peers.clone(),
2907 ValidationVersion::V3,
2908 )
2909 .await;
2910 }
2911 }
2912}
2913
2914pub(crate) async fn send_approvals_batched(
2916 sender: &mut impl overseer::SubsystemSender<NetworkBridgeTxMessage>,
2917 approvals: impl IntoIterator<Item = IndirectSignedApprovalVoteV2> + Clone,
2918 peers: &[(PeerId, ProtocolVersion)],
2919) {
2920 let v3_peers = filter_by_peer_version(peers, ValidationVersion::V3.into());
2921
2922 if !v3_peers.is_empty() {
2923 let mut batches = approvals.into_iter().peekable();
2924
2925 while batches.peek().is_some() {
2926 let batch: Vec<_> = batches.by_ref().take(MAX_APPROVAL_BATCH_SIZE).collect();
2927
2928 sender
2929 .send_message(NetworkBridgeTxMessage::SendValidationMessage(
2930 v3_peers.clone(),
2931 ValidationProtocols::V3(protocol_v3::ValidationProtocol::ApprovalDistribution(
2932 protocol_v3::ApprovalDistributionMessage::Approvals(batch),
2933 )),
2934 ))
2935 .await;
2936 }
2937 }
2938}