ckb_network/protocols/
support_protocols.rs

1use crate::ProtocolId;
2use p2p::{
3    builder::MetaBuilder,
4    service::{ProtocolHandle, ProtocolMeta},
5    traits::ServiceProtocol,
6};
7use tokio_util::codec::length_delimited;
8
9pub const LASTEST_VERSION: &str = "3";
10
11/// All supported protocols
12///
13/// The underlying network of CKB is flexible and complex. The flexibility lies in that it can support any number of protocols.
14/// Therefore, it is also relatively complex. Now, CKB has a bunch of protocols open by default,
15/// but not all protocols have to be open. In other words, if you want to interact with ckb nodes at the p2p layer,
16/// you only need to implement a few core protocols.
17///
18/// Core protocol: identify/discovery/sync/relay
19#[derive(Clone, Debug)]
20pub enum SupportProtocols {
21    /// Ping: as a health check for ping/pong
22    Ping,
23    /// Discovery: used to communicate with any node with any known node address,
24    /// to build a robust network topology as much as possible.
25    Discovery,
26    /// Identify: the first protocol opened when the nodes are interconnected,
27    /// used to obtain the features, versions, and observation addresses supported by the other node.
28    ///
29    /// [RFC](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0012-node-discovery/0012-node-discovery.md)
30    Identify,
31    /// Feeler: used to detect whether the address is valid.
32    ///
33    /// [RFC](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0007-scoring-system-and-network-security/0007-scoring-system-and-network-security.md#feeler-connection)
34    /// [Eclipse Attacks on Bitcoin's Peer-to-Peer Network](https://cryptographylab.bitbucket.io/slides/Eclipse%20Attacks%20on%20Bitcoin%27s%20Peer-to-Peer%20Network.pdf)
35    Feeler,
36    /// Disconnect message: used to give the remote node a debug message when the node decides to disconnect.
37    /// This message must be as quick as possible, otherwise the message may not be sent. So, use a separate protocol to support it.
38    DisconnectMessage,
39    /// Sync: ckb's main communication protocol for synchronize all blocks.
40    ///
41    /// [RFC](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0004-ckb-block-sync/0004-ckb-block-sync.md)
42    Sync,
43    /// Relay: ckb's main communication protocol for synchronizing latest transactions and blocks.
44    ///
45    /// [RFC](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0004-ckb-block-sync/0004-ckb-block-sync.md#new-block-announcement)
46    RelayV2,
47    /// Relay: ckb's main communication protocol for synchronizing latest transactions and blocks.
48    /// [RFC](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0004-ckb-block-sync/0004-ckb-block-sync.md#new-block-announcement)
49    RelayV3,
50    /// Time: A protocol used for node pairing that warns if there is a large gap between the local time and the remote node.
51    Time,
52    /// Alert: A protocol reserved by the Nervos Foundation to publish network-wide announcements.
53    /// Any information sent from the protocol is verified by multi-signature
54    Alert,
55    /// LightClient: A protocol used for light client.
56    LightClient,
57    /// Filter: A protocol used for client side block data filtering.
58    Filter,
59}
60
61impl SupportProtocols {
62    /// Protocol id
63    pub fn protocol_id(&self) -> ProtocolId {
64        match self {
65            SupportProtocols::Ping => 0,
66            SupportProtocols::Discovery => 1,
67            SupportProtocols::Identify => 2,
68            SupportProtocols::Feeler => 3,
69            SupportProtocols::DisconnectMessage => 4,
70            SupportProtocols::Sync => 100,
71            SupportProtocols::RelayV2 => 101,
72            SupportProtocols::RelayV3 => 103,
73            SupportProtocols::Time => 102,
74            SupportProtocols::Alert => 110,
75            SupportProtocols::LightClient => 120,
76            SupportProtocols::Filter => 121,
77        }
78        .into()
79    }
80
81    /// Protocol name
82    pub fn name(&self) -> String {
83        match self {
84            SupportProtocols::Ping => "/ckb/ping",
85            SupportProtocols::Discovery => "/ckb/discovery",
86            SupportProtocols::Identify => "/ckb/identify",
87            SupportProtocols::Feeler => "/ckb/flr",
88            SupportProtocols::DisconnectMessage => "/ckb/disconnectmsg",
89            SupportProtocols::Sync => "/ckb/syn",
90            SupportProtocols::RelayV2 => "/ckb/relay",
91            SupportProtocols::RelayV3 => "/ckb/relay3",
92            SupportProtocols::Time => "/ckb/tim",
93            SupportProtocols::Alert => "/ckb/alt",
94            SupportProtocols::LightClient => "/ckb/lightclient",
95            SupportProtocols::Filter => "/ckb/filter",
96        }
97        .to_owned()
98    }
99
100    /// Support versions
101    pub fn support_versions(&self) -> Vec<String> {
102        // Here you have to make sure that the list of supported versions is sorted from smallest to largest
103        match self {
104            SupportProtocols::Ping => vec!["2".to_owned(), LASTEST_VERSION.to_owned()],
105            SupportProtocols::Discovery => {
106                vec!["2".to_owned(), "2.1".to_owned(), LASTEST_VERSION.to_owned()]
107            }
108            SupportProtocols::Identify => vec!["2".to_owned(), LASTEST_VERSION.to_owned()],
109            SupportProtocols::Feeler => vec!["2".to_owned(), LASTEST_VERSION.to_owned()],
110            SupportProtocols::DisconnectMessage => {
111                vec!["2".to_owned(), LASTEST_VERSION.to_owned()]
112            }
113            SupportProtocols::Sync => vec!["2".to_owned(), LASTEST_VERSION.to_owned()],
114            SupportProtocols::Time => vec!["2".to_owned(), LASTEST_VERSION.to_owned()],
115            SupportProtocols::Alert => vec!["2".to_owned(), LASTEST_VERSION.to_owned()],
116            SupportProtocols::RelayV2 => vec!["2".to_owned(), LASTEST_VERSION.to_owned()],
117            SupportProtocols::RelayV3 => vec!["2".to_owned(), LASTEST_VERSION.to_owned()],
118            SupportProtocols::LightClient => vec!["2".to_owned(), LASTEST_VERSION.to_owned()],
119            SupportProtocols::Filter => vec!["2".to_owned(), LASTEST_VERSION.to_owned()],
120        }
121    }
122
123    /// Protocol message max length
124    pub fn max_frame_length(&self) -> usize {
125        match self {
126            SupportProtocols::Ping => 1024,              // 1   KB
127            SupportProtocols::Discovery => 512 * 1024,   // 512 KB
128            SupportProtocols::Identify => 2 * 1024,      // 2   KB
129            SupportProtocols::Feeler => 1024,            // 1   KB
130            SupportProtocols::DisconnectMessage => 1024, // 1   KB
131            SupportProtocols::Sync => 2 * 1024 * 1024,   // 2   MB
132            SupportProtocols::RelayV2 | SupportProtocols::RelayV3 => 4 * 1024 * 1024, // 4   MB
133            SupportProtocols::Time => 1024,              // 1   KB
134            SupportProtocols::Alert => 128 * 1024,       // 128 KB
135            SupportProtocols::LightClient => 2 * 1024 * 1024, // 2 MB
136            SupportProtocols::Filter => 2 * 1024 * 1024, // 2   MB
137        }
138    }
139
140    /// Builder with service handle
141    // a helper fn to build `ProtocolMeta`
142    pub fn build_meta_with_service_handle<
143        SH: FnOnce() -> ProtocolHandle<Box<dyn ServiceProtocol + Send + 'static + Unpin>>,
144    >(
145        self,
146        service_handle: SH,
147    ) -> ProtocolMeta {
148        let meta_builder: MetaBuilder = self.into();
149        meta_builder.service_handle(service_handle).build()
150    }
151}
152
153impl From<SupportProtocols> for MetaBuilder {
154    fn from(p: SupportProtocols) -> Self {
155        let max_frame_length = p.max_frame_length();
156        MetaBuilder::default()
157            .id(p.protocol_id())
158            .support_versions(p.support_versions())
159            .name(move |_| p.name())
160            .codec(move || {
161                Box::new(
162                    length_delimited::Builder::new()
163                        .max_frame_length(max_frame_length)
164                        .new_codec(),
165                )
166            })
167    }
168}