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