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 /// [RFC](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0004-ckb-block-sync/0004-ckb-block-sync.md#new-block-announcement)
45 RelayV3,
46 /// Time: A protocol used for node pairing that warns if there is a large gap between the local time and the remote node.
47 Time,
48 /// LightClient: A protocol used for light client.
49 LightClient,
50 /// Filter: A protocol used for client side block data filtering.
51 Filter,
52 /// HolePunching: A protocol used to connect peers behind firewalls or NAT routers.
53 HolePunching,
54}
55
56impl SupportProtocols {
57 /// Protocol id
58 pub fn protocol_id(&self) -> ProtocolId {
59 match self {
60 SupportProtocols::Ping => 0,
61 SupportProtocols::Discovery => 1,
62 SupportProtocols::Identify => 2,
63 SupportProtocols::Feeler => 3,
64 SupportProtocols::DisconnectMessage => 4,
65 SupportProtocols::Sync => 100,
66 SupportProtocols::RelayV3 => 101,
67 SupportProtocols::Time => 102,
68 SupportProtocols::LightClient => 120,
69 SupportProtocols::Filter => 121,
70 SupportProtocols::HolePunching => 130,
71 }
72 .into()
73 }
74
75 /// Protocol name
76 pub fn name(&self) -> String {
77 match self {
78 SupportProtocols::Ping => "/ckb/ping",
79 SupportProtocols::Discovery => "/ckb/discovery",
80 SupportProtocols::Identify => "/ckb/identify",
81 SupportProtocols::Feeler => "/ckb/flr",
82 SupportProtocols::DisconnectMessage => "/ckb/disconnectmsg",
83 SupportProtocols::Sync => "/ckb/syn",
84 SupportProtocols::RelayV3 => "/ckb/relay3",
85 SupportProtocols::Time => "/ckb/tim",
86 SupportProtocols::LightClient => "/ckb/lightclient",
87 SupportProtocols::Filter => "/ckb/filter",
88 SupportProtocols::HolePunching => "/ckb/holepunching",
89 }
90 .to_owned()
91 }
92
93 /// Support versions
94 pub fn support_versions(&self) -> Vec<String> {
95 // Here you have to make sure that the list of supported versions is sorted from smallest to largest
96 match self {
97 SupportProtocols::Ping => vec![LASTEST_VERSION.to_owned()],
98 SupportProtocols::Discovery => {
99 vec![LASTEST_VERSION.to_owned()]
100 }
101 SupportProtocols::Identify => vec![LASTEST_VERSION.to_owned()],
102 SupportProtocols::Feeler => vec![LASTEST_VERSION.to_owned()],
103 SupportProtocols::DisconnectMessage => {
104 vec![LASTEST_VERSION.to_owned()]
105 }
106 SupportProtocols::Sync => vec![LASTEST_VERSION.to_owned()],
107 SupportProtocols::Time => vec![LASTEST_VERSION.to_owned()],
108 SupportProtocols::RelayV3 => vec![LASTEST_VERSION.to_owned()],
109 SupportProtocols::LightClient => vec![LASTEST_VERSION.to_owned()],
110 SupportProtocols::Filter => vec![LASTEST_VERSION.to_owned()],
111 SupportProtocols::HolePunching => vec![LASTEST_VERSION.to_owned()],
112 }
113 }
114
115 /// Protocol message max length
116 pub fn max_frame_length(&self) -> usize {
117 match self {
118 SupportProtocols::Ping => 1024, // 1 KB
119 SupportProtocols::Discovery => 512 * 1024, // 512 KB
120 SupportProtocols::Identify => 2 * 1024, // 2 KB
121 SupportProtocols::Feeler => 1024, // 1 KB
122 SupportProtocols::DisconnectMessage => 1024, // 1 KB
123 SupportProtocols::Sync => 2 * 1024 * 1024, // 2 MB
124 SupportProtocols::RelayV3 => 4 * 1024 * 1024, // 4 MB
125 SupportProtocols::Time => 1024, // 1 KB
126 SupportProtocols::LightClient => 2 * 1024 * 1024, // 2 MB
127 SupportProtocols::Filter => 2 * 1024 * 1024, // 2 MB
128 SupportProtocols::HolePunching => 512 * 1024, // 512 KB
129 }
130 }
131
132 /// Builder with service handle
133 // a helper fn to build `ProtocolMeta`
134 pub fn build_meta_with_service_handle<
135 SH: FnOnce() -> ProtocolHandle<Box<dyn ServiceProtocol + Send + 'static + Unpin>>,
136 >(
137 self,
138 service_handle: SH,
139 ) -> ProtocolMeta {
140 let meta_builder: MetaBuilder = self.into();
141 meta_builder.service_handle(service_handle).build()
142 }
143}
144
145impl From<SupportProtocols> for MetaBuilder {
146 fn from(p: SupportProtocols) -> Self {
147 let max_frame_length = p.max_frame_length();
148 MetaBuilder::default()
149 .id(p.protocol_id())
150 .support_versions(p.support_versions())
151 .name(move |_| p.name())
152 .codec(move || {
153 Box::new(
154 length_delimited::Builder::new()
155 .max_frame_length(max_frame_length)
156 .new_codec(),
157 )
158 })
159 }
160}