ant_protocol/
version.rs

1// Copyright 2024 MaidSafe.net limited.
2//
3// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3.
4// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
5// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
6// KIND, either express or implied. Please review the Licences for the specific language governing
7// permissions and limitations relating to use of the SAFE Network Software.
8
9use std::sync::{LazyLock, RwLock};
10
11pub const MAINNET_ID: u8 = 1;
12pub const ALPHANET_ID: u8 = 2;
13
14/// Network identifier used to differentiate between different networks.
15///
16/// ## Default Value
17/// Set to `1` representing the mainnet network.
18///
19/// ## Usage
20/// - Network isolation mechanism
21/// - Prevents cross-network contamination
22/// - Used in protocol strings and user-agent identifiers
23pub static NETWORK_ID: LazyLock<RwLock<u8>> = LazyLock::new(|| RwLock::new(1));
24
25/// Node user-agent identifier for peer recognition and routing table management.
26///
27/// ## Purpose
28/// Functions as a user-agent identifier (similar to HTTP user-agent headers) that
29/// communicates peer type, its cargo package version, and network id to other peers.
30///
31/// ## Format
32/// `ant/node/{ant_protocol_version}/{ant_node_version}/{network_id}`
33///
34/// ## Behavior
35/// - Other nodes recognize this as a fellow routing participant
36/// - Peers with this identifier are added to routing tables (RT)
37pub fn construct_node_user_agent(node_version: String) -> String {
38    format!(
39        "ant/node/{ant_protocol_version}/{node_version}/{network_id}",
40        ant_protocol_version = get_truncate_version_str(),
41        network_id = get_network_id(),
42    )
43}
44
45/// Client user-agent identifier for peer recognition and routing exclusion.
46///
47/// ## Purpose
48/// Functions as a user-agent identifier (similar to HTTP user-agent headers) that
49/// communicates peer type, its cargo package version, and network id to other peers.
50///
51/// ## Format
52/// `ant/client/{ant_protocol_version}/{ant_client_version}/{network_id}`
53///
54/// ## Behavior
55/// - Nodes search for "client" in this identifier and they are **excluded** from routing tables (RT)
56/// - Treated as network consumers rather than routing participants
57pub fn construct_client_user_agent(client_version: String) -> String {
58    format!(
59        "ant/client/{ant_protocol_version}/{client_version}/{network_id}",
60        ant_protocol_version = get_truncate_version_str(),
61        network_id = get_network_id(),
62    )
63}
64
65/// The req/response protocol version
66///
67/// Defines the protocol identifier used for libp2p request-response communication that is used during
68/// libp2p's multistream-select negotiation. Both peers must use identical protocol strings to establish communication.
69///
70/// ## Format
71/// `/ant/{ant_protocol_version}/{network_id}`
72///
73/// ## Protocol Matching
74/// - **Match**: Both peers negotiate successfully, communication proceeds
75/// - **Mismatch**: Connection fails with `UnsupportedProtocols` error
76///   - Different versions cannot communicate
77///   - Different network IDs are isolated
78///   - Connection remains open for other protocols
79pub static REQ_RESPONSE_VERSION_STR: LazyLock<RwLock<String>> = LazyLock::new(|| {
80    RwLock::new(format!(
81        "/ant/{ant_protocol_version}/{network_id}",
82        ant_protocol_version = get_truncate_version_str(),
83        network_id = get_network_id()
84    ))
85});
86
87/// Identify protocol version string for peer compatibility verification.
88///
89/// ## Purpose
90/// Serves as a protocol handshake identifier ensuring peer communication compatibility.
91/// Both peers must have identical values to exchange messages successfully.
92///
93/// ## Format
94/// `ant/{ant_protocol_version}/{network_id}`
95///
96/// ## Compatibility Enforcement
97/// - **Compatible**: Peers with matching strings can communicate
98/// - **Incompatible**: Peers with different strings are:
99///   - Considered incompatible
100///   - Added to the blocklist
101///   - Protected against cross-network contamination
102pub static IDENTIFY_PROTOCOL_STR: LazyLock<RwLock<String>> = LazyLock::new(|| {
103    RwLock::new(format!(
104        "ant/{ant_protocol_version}/{network_id}",
105        ant_protocol_version = get_truncate_version_str(),
106        network_id = get_network_id(),
107    ))
108});
109
110/// Update the NETWORK_ID.
111///
112/// Other version strings will reference this value. The default is 1, representing the mainnet.
113///
114/// This function should be used sparingly, ideally before the node or client is started.
115///
116/// Each of the version strings need to be explicitly updated here. There are scenarios where they
117/// could be read before this function is called, in which case they will have the old value for
118/// the lifetime of the program.
119pub fn set_network_id(id: u8) {
120    info!("Setting network id to: {id}");
121    {
122        let mut network_id = NETWORK_ID
123            .write()
124            .expect("Failed to obtain write lock for NETWORK_ID");
125        *network_id = id;
126    }
127
128    {
129        let mut req_response = REQ_RESPONSE_VERSION_STR
130            .write()
131            .expect("Failed to obtain write lock for REQ_RESPONSE_VERSION_STR");
132        *req_response = format!("/ant/{}/{}", get_truncate_version_str(), id);
133    }
134
135    {
136        let mut identify_protocol = IDENTIFY_PROTOCOL_STR
137            .write()
138            .expect("Failed to obtain write lock for IDENTIFY_PROTOCOL_STR");
139        *identify_protocol = format!("ant/{}/{}", get_truncate_version_str(), id);
140    }
141
142    info!("Network id set to: {id} and all protocol strings updated");
143}
144
145/// Get the current NETWORK_ID as string.
146pub fn get_network_id() -> u8 {
147    *NETWORK_ID
148        .read()
149        .expect("Failed to obtain read lock for NETWORK_ID")
150}
151
152/// Get the current NETWORK_ID as string.
153pub fn get_network_id_str() -> String {
154    format!(
155        "{}",
156        *NETWORK_ID
157            .read()
158            .expect("Failed to obtain read lock for NETWORK_ID")
159    )
160}
161
162// Protocol support shall be downward compatible for patch only version update.
163// i.e. versions of `A.B.X` or `A.B.X-alpha.Y` shall be considered as a same protocol of `A.B`
164pub fn get_truncate_version_str() -> String {
165    let version_str = env!("CARGO_PKG_VERSION").to_string();
166    let parts = version_str.split('.').collect::<Vec<_>>();
167    if parts.len() >= 2 {
168        format!("{}.{}", parts[0], parts[1])
169    } else {
170        panic!("Cannot obtain truncated version str for {version_str:?}: {parts:?}");
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use super::*;
177
178    #[test]
179    fn test_print_version_strings() -> Result<(), Box<dyn std::error::Error>> {
180        set_network_id(3);
181        println!(
182            "\nNode user agent: {}",
183            construct_node_user_agent("1.0.0".to_string())
184        );
185        println!(
186            "Client user agent: {}",
187            construct_client_user_agent("1.0.0".to_string())
188        );
189        println!(
190            "REQ_RESPONSE_VERSION_STR: {}",
191            *REQ_RESPONSE_VERSION_STR.read().expect(
192                "Failed to 
193                obtain read lock for REQ_RESPONSE_VERSION_STR"
194            )
195        );
196        println!(
197            "IDENTIFY_PROTOCOL_STR: {}",
198            *IDENTIFY_PROTOCOL_STR
199                .read()
200                .expect("Failed to obtain read lock for IDENTIFY_PROTOCOL_STR")
201        );
202
203        // Test truncated version string
204        let truncated = get_truncate_version_str();
205        println!("\nTruncated version: {truncated}");
206
207        // Test network id string
208        let network_id = get_network_id_str();
209        println!("Network ID string: {network_id}");
210
211        Ok(())
212    }
213}