1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
4pub struct CapabilityDoc {
5 pub protocol: u16,
6 pub max_frame: u32,
7 pub max_drop: u64,
8 pub chunking: Vec<String>,
9 pub compression: Vec<String>,
10 pub inventory: Vec<String>,
11 pub routing: Vec<String>,
12 pub receipts: bool,
13 pub extensions: Vec<String>,
14 pub relay_capacity: RelayCapacity,
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
18#[serde(rename_all = "snake_case")]
19pub enum RelayCapacity {
20 #[default]
21 Full,
22 Low,
23 None,
24}
25
26impl CapabilityDoc {
27 pub fn local_v2() -> Self {
28 Self {
29 protocol: crate::PROTOCOL_VERSION,
30 max_frame: crate::limits::MAX_FRAME_SIZE,
31 max_drop: crate::limits::MAX_DROP_SIZE,
32 chunking: vec!["fixed".into(), "cdc-v1".into(), "erasure-xor-v1".into()],
33 compression: vec!["none".into(), "zstd".into()],
34 inventory: vec!["sorted-v1".into(), "bloom-v1".into()],
35 routing: vec![
36 "direct".into(),
37 "epidemic".into(),
38 "spray".into(),
39 "encounter".into(),
40 "adaptive".into(),
41 ],
42 receipts: true,
43 extensions: vec![
44 "dd.group/1".into(),
45 "sealed-drop-v1".into(),
46 "erasure-v1".into(),
47 "receipt-v2".into(),
48 "spaces-v1".into(),
49 "quic-v1".into(),
50 "lan-v1".into(),
51 ],
52 relay_capacity: RelayCapacity::Full,
53 }
54 }
55
56 pub fn negotiate(&self, other: &Self) -> crate::Result<NegotiatedCaps> {
58 if other.protocol != self.protocol {
59 return Err(crate::DdError::protocol(
60 crate::ErrorCode::Ddp1002UnsupportedVersion,
61 format!("peer speaks DDP/{}", other.protocol),
62 ));
63 }
64 Ok(NegotiatedCaps {
65 protocol: self.protocol,
66 max_frame: self.max_frame.min(other.max_frame),
67 max_drop: self.max_drop.min(other.max_drop),
68 chunking: intersect(&self.chunking, &other.chunking),
69 compression: intersect(&self.compression, &other.compression),
70 inventory: intersect(&self.inventory, &other.inventory),
71 routing: intersect(&self.routing, &other.routing),
72 receipts: self.receipts && other.receipts,
73 peer_relay: other.relay_capacity,
74 })
75 }
76}
77
78fn intersect(a: &[String], b: &[String]) -> Vec<String> {
79 a.iter().filter(|x| b.contains(x)).cloned().collect()
80}
81
82#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
83pub struct NegotiatedCaps {
84 pub protocol: u16,
85 pub max_frame: u32,
86 pub max_drop: u64,
87 pub chunking: Vec<String>,
88 pub compression: Vec<String>,
89 pub inventory: Vec<String>,
90 pub routing: Vec<String>,
91 pub receipts: bool,
92 pub peer_relay: RelayCapacity,
93}
94
95impl NegotiatedCaps {
96 pub fn prefer_inventory(&self) -> &'static str {
97 if self.inventory.iter().any(|s| s == "bloom-v1") {
98 "bloom-v1"
99 } else {
100 "sorted-v1"
101 }
102 }
103}