1use derive_more::Display;
20use polkadot_primitives::Hash;
21use sc_network::{
22 config::SetConfig, peer_store::PeerStoreProvider, service::NotificationMetrics,
23 types::ProtocolName, NetworkBackend, NotificationService,
24};
25use sp_runtime::traits::Block;
26use std::{
27 collections::{hash_map::Entry, HashMap},
28 ops::{Index, IndexMut},
29 sync::Arc,
30};
31use strum::{EnumIter, IntoEnumIterator};
32
33const LEGACY_COLLATION_PROTOCOL_V1: &str = "/polkadot/collation/1";
35
36const LEGACY_COLLATION_PROTOCOL_VERSION_V1: u32 = 1;
38
39pub const MAX_NOTIFICATION_SIZE: u64 = 100 * 1024;
41
42pub const MAX_AUTHORITY_INCOMING_STREAMS: u32 = 100;
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumIter)]
47pub enum PeerSet {
48 Validation,
51 Collation,
53}
54
55#[derive(Copy, Clone, Debug, Eq, PartialEq)]
59pub enum IsAuthority {
60 Yes,
62 No,
64}
65
66impl PeerSet {
67 pub fn get_info<B: Block, N: NetworkBackend<B, <B as Block>::Hash>>(
72 self,
73 is_authority: IsAuthority,
74 peerset_protocol_names: &PeerSetProtocolNames,
75 metrics: NotificationMetrics,
76 peer_store_handle: Arc<dyn PeerStoreProvider>,
77 ) -> (N::NotificationProtocolConfig, (PeerSet, Box<dyn NotificationService>)) {
78 let protocol = peerset_protocol_names.get_main_name(self);
81 let fallback_names = PeerSetProtocolNames::get_fallback_names(
82 self,
83 &peerset_protocol_names.genesis_hash,
84 peerset_protocol_names.fork_id.as_deref(),
85 );
86 let max_notification_size = self.get_max_notification_size(is_authority);
87
88 match self {
89 PeerSet::Validation => {
90 let (config, notification_service) = N::notification_config(
91 protocol,
92 fallback_names,
93 max_notification_size,
94 None,
95 SetConfig {
96 in_peers: super::MIN_GOSSIP_PEERS as u32 / 2 - 1,
101 out_peers: super::MIN_GOSSIP_PEERS as u32 / 2 - 1,
102 reserved_nodes: Vec::new(),
103 non_reserved_mode: sc_network::config::NonReservedPeerMode::Accept,
104 },
105 metrics,
106 peer_store_handle,
107 );
108
109 (config, (PeerSet::Validation, notification_service))
110 },
111 PeerSet::Collation => {
112 let (config, notification_service) = N::notification_config(
113 protocol,
114 fallback_names,
115 max_notification_size,
116 None,
117 SetConfig {
118 in_peers: if is_authority == IsAuthority::Yes {
121 MAX_AUTHORITY_INCOMING_STREAMS
122 } else {
123 0
124 },
125 out_peers: 0,
126 reserved_nodes: Vec::new(),
127 non_reserved_mode: if is_authority == IsAuthority::Yes {
128 sc_network::config::NonReservedPeerMode::Accept
129 } else {
130 sc_network::config::NonReservedPeerMode::Deny
131 },
132 },
133 metrics,
134 peer_store_handle,
135 );
136
137 (config, (PeerSet::Collation, notification_service))
138 },
139 }
140 }
141
142 pub fn get_main_version(self) -> ProtocolVersion {
147 match self {
148 PeerSet::Validation => ValidationVersion::V3.into(),
149 PeerSet::Collation => CollationVersion::V2.into(),
150 }
151 }
152
153 pub fn get_max_notification_size(self, _: IsAuthority) -> u64 {
155 MAX_NOTIFICATION_SIZE
156 }
157
158 pub fn get_label(self) -> &'static str {
160 match self {
161 PeerSet::Validation => "validation",
162 PeerSet::Collation => "collation",
163 }
164 }
165
166 pub fn get_protocol_label(self, version: ProtocolVersion) -> Option<&'static str> {
168 match self {
171 PeerSet::Validation =>
172 if version == ValidationVersion::V3.into() {
173 Some("validation/3")
174 } else {
175 None
176 },
177 PeerSet::Collation =>
178 if version == CollationVersion::V1.into() {
179 Some("collation/1")
180 } else if version == CollationVersion::V2.into() {
181 Some("collation/2")
182 } else {
183 None
184 },
185 }
186 }
187}
188
189#[derive(Debug, Default)]
191pub struct PerPeerSet<T> {
192 validation: T,
193 collation: T,
194}
195
196impl<T> Index<PeerSet> for PerPeerSet<T> {
197 type Output = T;
198 fn index(&self, index: PeerSet) -> &T {
199 match index {
200 PeerSet::Validation => &self.validation,
201 PeerSet::Collation => &self.collation,
202 }
203 }
204}
205
206impl<T> IndexMut<PeerSet> for PerPeerSet<T> {
207 fn index_mut(&mut self, index: PeerSet) -> &mut T {
208 match index {
209 PeerSet::Validation => &mut self.validation,
210 PeerSet::Collation => &mut self.collation,
211 }
212 }
213}
214
215pub fn peer_sets_info<B: Block, N: NetworkBackend<B, <B as Block>::Hash>>(
220 is_authority: IsAuthority,
221 peerset_protocol_names: &PeerSetProtocolNames,
222 metrics: NotificationMetrics,
223 peer_store_handle: Arc<dyn PeerStoreProvider>,
224) -> Vec<(N::NotificationProtocolConfig, (PeerSet, Box<dyn NotificationService>))> {
225 PeerSet::iter()
226 .map(|s| {
227 s.get_info::<B, N>(
228 is_authority,
229 &peerset_protocol_names,
230 metrics.clone(),
231 Arc::clone(&peer_store_handle),
232 )
233 })
234 .collect()
235}
236
237#[derive(Debug, Clone, Copy, Display, PartialEq, Eq, Hash)]
239pub struct ProtocolVersion(u32);
240
241impl From<ProtocolVersion> for u32 {
242 fn from(version: ProtocolVersion) -> u32 {
243 version.0
244 }
245}
246
247#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumIter)]
249pub enum ValidationVersion {
250 V3 = 3,
252}
253
254#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumIter)]
256pub enum CollationVersion {
257 V1 = 1,
259 V2 = 2,
261}
262
263#[derive(Debug, Clone, Copy, PartialEq, Eq)]
265pub struct UnknownVersion;
266
267impl TryFrom<ProtocolVersion> for ValidationVersion {
268 type Error = UnknownVersion;
269
270 fn try_from(p: ProtocolVersion) -> Result<Self, UnknownVersion> {
271 for v in Self::iter() {
272 if v as u32 == p.0 {
273 return Ok(v)
274 }
275 }
276
277 Err(UnknownVersion)
278 }
279}
280
281impl TryFrom<ProtocolVersion> for CollationVersion {
282 type Error = UnknownVersion;
283
284 fn try_from(p: ProtocolVersion) -> Result<Self, UnknownVersion> {
285 for v in Self::iter() {
286 if v as u32 == p.0 {
287 return Ok(v)
288 }
289 }
290
291 Err(UnknownVersion)
292 }
293}
294
295impl From<ValidationVersion> for ProtocolVersion {
296 fn from(version: ValidationVersion) -> ProtocolVersion {
297 ProtocolVersion(version as u32)
298 }
299}
300
301impl From<CollationVersion> for ProtocolVersion {
302 fn from(version: CollationVersion) -> ProtocolVersion {
303 ProtocolVersion(version as u32)
304 }
305}
306
307#[derive(Debug, Clone)]
309pub struct PeerSetProtocolNames {
310 protocols: HashMap<ProtocolName, (PeerSet, ProtocolVersion)>,
311 names: HashMap<(PeerSet, ProtocolVersion), ProtocolName>,
312 genesis_hash: Hash,
313 fork_id: Option<String>,
314}
315
316impl PeerSetProtocolNames {
317 pub fn new(genesis_hash: Hash, fork_id: Option<&str>) -> Self {
319 let mut protocols = HashMap::new();
320 let mut names = HashMap::new();
321 for protocol in PeerSet::iter() {
322 match protocol {
323 PeerSet::Validation =>
324 for version in ValidationVersion::iter() {
325 Self::register_main_protocol(
326 &mut protocols,
327 &mut names,
328 protocol,
329 version.into(),
330 &genesis_hash,
331 fork_id,
332 );
333 },
334 PeerSet::Collation => {
335 for version in CollationVersion::iter() {
336 Self::register_main_protocol(
337 &mut protocols,
338 &mut names,
339 protocol,
340 version.into(),
341 &genesis_hash,
342 fork_id,
343 );
344 }
345 Self::register_legacy_collation_protocol(&mut protocols, protocol);
346 },
347 }
348 }
349 Self { protocols, names, genesis_hash, fork_id: fork_id.map(|fork_id| fork_id.into()) }
350 }
351
352 fn register_main_protocol(
354 protocols: &mut HashMap<ProtocolName, (PeerSet, ProtocolVersion)>,
355 names: &mut HashMap<(PeerSet, ProtocolVersion), ProtocolName>,
356 protocol: PeerSet,
357 version: ProtocolVersion,
358 genesis_hash: &Hash,
359 fork_id: Option<&str>,
360 ) {
361 let protocol_name = Self::generate_name(genesis_hash, fork_id, protocol, version);
362 names.insert((protocol, version), protocol_name.clone());
363 Self::insert_protocol_or_panic(protocols, protocol_name, protocol, version);
364 }
365
366 fn register_legacy_collation_protocol(
368 protocols: &mut HashMap<ProtocolName, (PeerSet, ProtocolVersion)>,
369 protocol: PeerSet,
370 ) {
371 Self::insert_protocol_or_panic(
372 protocols,
373 LEGACY_COLLATION_PROTOCOL_V1.into(),
374 protocol,
375 ProtocolVersion(LEGACY_COLLATION_PROTOCOL_VERSION_V1),
376 )
377 }
378
379 fn insert_protocol_or_panic(
381 protocols: &mut HashMap<ProtocolName, (PeerSet, ProtocolVersion)>,
382 name: ProtocolName,
383 protocol: PeerSet,
384 version: ProtocolVersion,
385 ) {
386 match protocols.entry(name) {
387 Entry::Vacant(entry) => {
388 entry.insert((protocol, version));
389 },
390 Entry::Occupied(entry) => {
391 panic!(
392 "Protocol {:?} (version {}) has the same on-the-wire name as protocol {:?} (version {}): `{}`.",
393 protocol,
394 version,
395 entry.get().0,
396 entry.get().1,
397 entry.key(),
398 );
399 },
400 }
401 }
402
403 pub fn try_get_protocol(&self, name: &ProtocolName) -> Option<(PeerSet, ProtocolVersion)> {
405 self.protocols.get(name).map(ToOwned::to_owned)
406 }
407
408 pub fn get_main_name(&self, protocol: PeerSet) -> ProtocolName {
411 self.get_name(protocol, protocol.get_main_version())
412 }
413
414 pub fn get_name(&self, protocol: PeerSet, version: ProtocolVersion) -> ProtocolName {
416 self.names
417 .get(&(protocol, version))
418 .expect("Protocols & versions are specified via enums defined above, and they are all registered in `new()`; qed")
419 .clone()
420 }
421
422 fn generate_name(
424 genesis_hash: &Hash,
425 fork_id: Option<&str>,
426 protocol: PeerSet,
427 version: ProtocolVersion,
428 ) -> ProtocolName {
429 let prefix = if let Some(fork_id) = fork_id {
430 format!("/{}/{}", hex::encode(genesis_hash), fork_id)
431 } else {
432 format!("/{}", hex::encode(genesis_hash))
433 };
434
435 let short_name = match protocol {
436 PeerSet::Validation => "validation",
437 PeerSet::Collation => "collation",
438 };
439
440 format!("{}/{}/{}", prefix, short_name, version).into()
441 }
442
443 fn get_fallback_names(
446 protocol: PeerSet,
447 _genesis_hash: &Hash,
448 _fork_id: Option<&str>,
449 ) -> Vec<ProtocolName> {
450 let mut fallbacks = vec![];
451 match protocol {
452 PeerSet::Validation => {
453 },
456 PeerSet::Collation => {
457 fallbacks.push(LEGACY_COLLATION_PROTOCOL_V1.into());
458 },
459 };
460 fallbacks
461 }
462}
463
464#[cfg(test)]
465mod tests {
466 use super::{
467 CollationVersion, Hash, PeerSet, PeerSetProtocolNames, ProtocolVersion, ValidationVersion,
468 };
469 use strum::IntoEnumIterator;
470
471 struct TestVersion(u32);
472
473 impl From<TestVersion> for ProtocolVersion {
474 fn from(version: TestVersion) -> ProtocolVersion {
475 ProtocolVersion(version.0)
476 }
477 }
478
479 #[test]
480 fn protocol_names_are_correctly_generated() {
481 let genesis_hash = Hash::from([
482 122, 200, 116, 29, 232, 183, 20, 109, 138, 86, 23, 253, 70, 41, 20, 85, 127, 230, 60,
483 38, 90, 127, 28, 16, 231, 218, 227, 40, 88, 238, 187, 128,
484 ]);
485 let name = PeerSetProtocolNames::generate_name(
486 &genesis_hash,
487 None,
488 PeerSet::Validation,
489 TestVersion(3).into(),
490 );
491 let expected =
492 "/7ac8741de8b7146d8a5617fd462914557fe63c265a7f1c10e7dae32858eebb80/validation/3";
493 assert_eq!(name, expected.into());
494
495 let name = PeerSetProtocolNames::generate_name(
496 &genesis_hash,
497 None,
498 PeerSet::Collation,
499 TestVersion(5).into(),
500 );
501 let expected =
502 "/7ac8741de8b7146d8a5617fd462914557fe63c265a7f1c10e7dae32858eebb80/collation/5";
503 assert_eq!(name, expected.into());
504
505 let fork_id = Some("test-fork");
506 let name = PeerSetProtocolNames::generate_name(
507 &genesis_hash,
508 fork_id,
509 PeerSet::Validation,
510 TestVersion(7).into(),
511 );
512 let expected =
513 "/7ac8741de8b7146d8a5617fd462914557fe63c265a7f1c10e7dae32858eebb80/test-fork/validation/7";
514 assert_eq!(name, expected.into());
515
516 let name = PeerSetProtocolNames::generate_name(
517 &genesis_hash,
518 fork_id,
519 PeerSet::Collation,
520 TestVersion(11).into(),
521 );
522 let expected =
523 "/7ac8741de8b7146d8a5617fd462914557fe63c265a7f1c10e7dae32858eebb80/test-fork/collation/11";
524 assert_eq!(name, expected.into());
525 }
526
527 #[test]
528 fn all_protocol_names_are_known() {
529 let genesis_hash = Hash::from([
530 122, 200, 116, 29, 232, 183, 20, 109, 138, 86, 23, 253, 70, 41, 20, 85, 127, 230, 60,
531 38, 90, 127, 28, 16, 231, 218, 227, 40, 88, 238, 187, 128,
532 ]);
533 let protocol_names = PeerSetProtocolNames::new(genesis_hash, None);
534
535 let validation_main =
536 "/7ac8741de8b7146d8a5617fd462914557fe63c265a7f1c10e7dae32858eebb80/validation/3";
537 assert_eq!(
538 protocol_names.try_get_protocol(&validation_main.into()),
539 Some((PeerSet::Validation, TestVersion(3).into())),
540 );
541
542 let validation_legacy = "/polkadot/validation/1";
543 assert!(protocol_names.try_get_protocol(&validation_legacy.into()).is_none());
544
545 let collation_main =
546 "/7ac8741de8b7146d8a5617fd462914557fe63c265a7f1c10e7dae32858eebb80/collation/1";
547 assert_eq!(
548 protocol_names.try_get_protocol(&collation_main.into()),
549 Some((PeerSet::Collation, TestVersion(1).into())),
550 );
551
552 let collation_legacy = "/polkadot/collation/1";
553 assert_eq!(
554 protocol_names.try_get_protocol(&collation_legacy.into()),
555 Some((PeerSet::Collation, TestVersion(1).into())),
556 );
557 }
558
559 #[test]
560 fn all_protocol_versions_are_registered() {
561 let genesis_hash = Hash::from([
562 122, 200, 116, 29, 232, 183, 20, 109, 138, 86, 23, 253, 70, 41, 20, 85, 127, 230, 60,
563 38, 90, 127, 28, 16, 231, 218, 227, 40, 88, 238, 187, 128,
564 ]);
565 let protocol_names = PeerSetProtocolNames::new(genesis_hash, None);
566
567 for protocol in PeerSet::iter() {
568 match protocol {
569 PeerSet::Validation =>
570 for version in ValidationVersion::iter() {
571 assert_eq!(
572 protocol_names.get_name(protocol, version.into()),
573 PeerSetProtocolNames::generate_name(
574 &genesis_hash,
575 None,
576 protocol,
577 version.into(),
578 ),
579 );
580 },
581 PeerSet::Collation =>
582 for version in CollationVersion::iter() {
583 assert_eq!(
584 protocol_names.get_name(protocol, version.into()),
585 PeerSetProtocolNames::generate_name(
586 &genesis_hash,
587 None,
588 protocol,
589 version.into(),
590 ),
591 );
592 },
593 }
594 }
595 }
596
597 #[test]
598 fn all_protocol_versions_have_labels() {
599 for protocol in PeerSet::iter() {
600 match protocol {
601 PeerSet::Validation =>
602 for version in ValidationVersion::iter() {
603 protocol
604 .get_protocol_label(version.into())
605 .expect("All validation protocol versions must have a label.");
606 },
607 PeerSet::Collation =>
608 for version in CollationVersion::iter() {
609 protocol
610 .get_protocol_label(version.into())
611 .expect("All collation protocol versions must have a label.");
612 },
613 }
614 }
615 }
616}