Skip to main content

ant_protocol/
lib.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
9// Allow enum variant names that end with Error as they come from thiserror derives
10#![allow(clippy::enum_variant_names)]
11// Allow the to_* methods on Copy types (used for byte conversions)
12#![allow(clippy::wrong_self_convention)]
13// Allow expect/panic usage temporarily
14#![allow(clippy::expect_used)]
15#![allow(clippy::panic)]
16
17#[macro_use]
18extern crate tracing;
19
20/// Constants
21pub mod constants;
22/// Errors.
23pub mod error;
24/// Messages types
25pub mod messages;
26/// RPC commands to node
27pub mod node_rpc;
28/// `PeerId` serialization helpers
29pub(crate) mod peer_id_serde;
30/// Storage types for GraphEntry and Chunk
31pub mod storage;
32/// Network versioning
33pub mod version;
34/// Version gating for peer validation
35pub mod version_gate;
36
37// this includes code generated from .proto files
38#[expect(clippy::unwrap_used, clippy::clone_on_ref_ptr)]
39#[cfg(feature = "rpc")]
40pub mod antnode_proto {
41    tonic::include_proto!("antnode_proto");
42}
43pub use error::Error;
44pub use error::Error as NetworkError;
45
46use self::storage::{ChunkAddress, GraphEntryAddress, PointerAddress, ScratchpadAddress};
47
48/// Re-export of Bytes used throughout the protocol
49pub use bytes::Bytes;
50
51use libp2p::{
52    Multiaddr, PeerId,
53    kad::{KBucketDistance as Distance, KBucketKey as Key, RecordKey},
54    multiaddr::Protocol,
55};
56use serde::{Deserialize, Deserializer, Serialize, Serializer};
57use std::{
58    borrow::Cow,
59    fmt::{self, Debug, Display, Formatter, Write},
60};
61use xor_name::XorName;
62
63pub use constants::CLOSE_GROUP_SIZE;
64
65/// Returns the UDP port from the provided MultiAddr.
66pub fn get_port_from_multiaddr(multi_addr: &Multiaddr) -> Option<u16> {
67    // assuming the listening addr contains /ip4/127.0.0.1/udp/56215/quic-v1/p2p/<peer_id>
68    for protocol in multi_addr.iter() {
69        if let Protocol::Udp(port) = protocol {
70            return Some(port);
71        }
72    }
73    None
74}
75
76/// This is the address in the network by which proximity/distance
77/// to other items (whether nodes or data chunks) are calculated.
78///
79/// This is the mapping from the XOR name used
80/// by for example self encryption, or the libp2p `PeerId`,
81/// to the key used in the Kademlia DHT.
82/// All our xorname calculations shall be replaced with the `KBucketKey` calculations,
83/// for getting proximity/distance to other items (whether nodes or data).
84#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
85pub enum NetworkAddress {
86    /// The NetworkAddress is representing a PeerId.
87    PeerId(Bytes),
88    /// The NetworkAddress is representing a ChunkAddress.
89    ChunkAddress(ChunkAddress),
90    /// The NetworkAddress is representing a GraphEntryAddress.
91    GraphEntryAddress(GraphEntryAddress),
92    /// The NetworkAddress is representing a ScratchpadAddress.
93    ScratchpadAddress(ScratchpadAddress),
94    /// The NetworkAddress is representing a PointerAddress.
95    PointerAddress(PointerAddress),
96    /// The NetworkAddress is representing a RecordKey.
97    RecordKey(Bytes),
98}
99
100impl NetworkAddress {
101    /// Return the encapsulated bytes of this `NetworkAddress`.
102    pub fn as_bytes(&self) -> Vec<u8> {
103        match self {
104            NetworkAddress::PeerId(bytes) | NetworkAddress::RecordKey(bytes) => bytes.to_vec(),
105            NetworkAddress::ChunkAddress(chunk_address) => chunk_address.xorname().to_vec(),
106            NetworkAddress::GraphEntryAddress(graph_entry_address) => {
107                graph_entry_address.xorname().to_vec()
108            }
109            NetworkAddress::ScratchpadAddress(addr) => addr.xorname().to_vec(),
110            NetworkAddress::PointerAddress(pointer_address) => pointer_address.xorname().to_vec(),
111        }
112    }
113
114    /// Return the XorName represented by this `NetworkAddress`.
115    pub fn xorname(&self) -> Option<XorName> {
116        match self {
117            NetworkAddress::ChunkAddress(chunk_address) => Some(*chunk_address.xorname()),
118            NetworkAddress::GraphEntryAddress(graph_entry_address) => {
119                Some(graph_entry_address.xorname())
120            }
121            NetworkAddress::ScratchpadAddress(scratchpad_address) => {
122                Some(scratchpad_address.xorname())
123            }
124            NetworkAddress::PointerAddress(pointer_address) => Some(pointer_address.xorname()),
125            NetworkAddress::PeerId(_) => None,
126            NetworkAddress::RecordKey(_) => None,
127        }
128    }
129
130    /// Try to return the represented `PeerId`.
131    pub fn as_peer_id(&self) -> Option<PeerId> {
132        if let NetworkAddress::PeerId(bytes) = self
133            && let Ok(peer_id) = PeerId::from_bytes(bytes)
134        {
135            return Some(peer_id);
136        }
137        None
138    }
139
140    /// Return the convertable `RecordKey`.
141    pub fn to_record_key(&self) -> RecordKey {
142        match self {
143            NetworkAddress::RecordKey(bytes) => RecordKey::new(bytes),
144            NetworkAddress::ChunkAddress(chunk_address) => RecordKey::new(chunk_address.xorname()),
145            NetworkAddress::GraphEntryAddress(graph_entry_address) => {
146                RecordKey::new(&graph_entry_address.xorname())
147            }
148            NetworkAddress::PointerAddress(pointer_address) => {
149                RecordKey::new(&pointer_address.xorname())
150            }
151            NetworkAddress::ScratchpadAddress(addr) => RecordKey::new(&addr.xorname()),
152            NetworkAddress::PeerId(bytes) => RecordKey::new(bytes),
153        }
154    }
155
156    /// Return the `KBucketKey` representation of this `NetworkAddress`.
157    ///
158    /// The `KBucketKey` is used for calculating proximity/distance to other items (whether nodes or data).
159    /// Important to note is that it will always SHA256 hash any bytes it receives.
160    /// Therefore, the canonical use of distance/proximity calculations in the network
161    /// is via the `KBucketKey`, or the convenience methods of `NetworkAddress`.
162    pub fn as_kbucket_key(&self) -> Key<Vec<u8>> {
163        Key::new(self.as_bytes())
164    }
165
166    /// Compute the distance of the keys according to the XOR metric.
167    pub fn distance(&self, other: &NetworkAddress) -> Distance {
168        self.as_kbucket_key().distance(&other.as_kbucket_key())
169    }
170}
171
172impl From<XorName> for NetworkAddress {
173    fn from(xorname: XorName) -> Self {
174        NetworkAddress::ChunkAddress(ChunkAddress::new(xorname))
175    }
176}
177
178impl From<ChunkAddress> for NetworkAddress {
179    fn from(chunk_address: ChunkAddress) -> Self {
180        NetworkAddress::ChunkAddress(chunk_address)
181    }
182}
183
184impl From<GraphEntryAddress> for NetworkAddress {
185    fn from(graph_entry_address: GraphEntryAddress) -> Self {
186        NetworkAddress::GraphEntryAddress(graph_entry_address)
187    }
188}
189
190impl From<ScratchpadAddress> for NetworkAddress {
191    fn from(scratchpad_address: ScratchpadAddress) -> Self {
192        NetworkAddress::ScratchpadAddress(scratchpad_address)
193    }
194}
195
196impl From<PointerAddress> for NetworkAddress {
197    fn from(pointer_address: PointerAddress) -> Self {
198        NetworkAddress::PointerAddress(pointer_address)
199    }
200}
201
202impl From<PeerId> for NetworkAddress {
203    fn from(peer_id: PeerId) -> Self {
204        NetworkAddress::PeerId(Bytes::from(peer_id.to_bytes()))
205    }
206}
207
208impl From<&RecordKey> for NetworkAddress {
209    fn from(record_key: &RecordKey) -> Self {
210        NetworkAddress::RecordKey(Bytes::copy_from_slice(record_key.as_ref()))
211    }
212}
213
214impl Debug for NetworkAddress {
215    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
216        let name_str = match self {
217            NetworkAddress::PeerId(_) => {
218                if let Some(peer_id) = self.as_peer_id() {
219                    format!("NetworkAddress::{peer_id:?} - (")
220                } else {
221                    "NetworkAddress::PeerId(".to_string()
222                }
223            }
224            NetworkAddress::ChunkAddress(chunk_address) => {
225                format!(
226                    "NetworkAddress::ChunkAddress({}) - (",
227                    &chunk_address.to_hex()
228                )
229            }
230            NetworkAddress::GraphEntryAddress(graph_entry_address) => {
231                format!(
232                    "NetworkAddress::GraphEntryAddress({}) - (",
233                    &graph_entry_address.to_hex()
234                )
235            }
236            NetworkAddress::ScratchpadAddress(scratchpad_address) => {
237                format!(
238                    "NetworkAddress::ScratchpadAddress({}) - (",
239                    &scratchpad_address.to_hex()
240                )
241            }
242            NetworkAddress::PointerAddress(pointer_address) => {
243                format!(
244                    "NetworkAddress::PointerAddress({}) - (",
245                    &pointer_address.to_hex()
246                )
247            }
248            NetworkAddress::RecordKey(bytes) => {
249                format!("NetworkAddress::RecordKey({:?}) - (", hex::encode(bytes))
250            }
251        };
252
253        write!(
254            f,
255            "{name_str}{:?})",
256            PrettyPrintKBucketKey(self.as_kbucket_key())
257        )
258    }
259}
260
261impl Display for NetworkAddress {
262    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
263        match self {
264            NetworkAddress::PeerId(id) => {
265                write!(f, "NetworkAddress::PeerId({})", hex::encode(id))
266            }
267            NetworkAddress::ChunkAddress(addr) => {
268                write!(f, "NetworkAddress::ChunkAddress({addr})")
269            }
270            NetworkAddress::GraphEntryAddress(addr) => {
271                write!(f, "NetworkAddress::GraphEntryAddress({addr})")
272            }
273            NetworkAddress::ScratchpadAddress(addr) => {
274                write!(f, "NetworkAddress::ScratchpadAddress({addr})")
275            }
276            NetworkAddress::RecordKey(key) => {
277                write!(f, "NetworkAddress::RecordKey({})", hex::encode(key))
278            }
279            NetworkAddress::PointerAddress(addr) => {
280                write!(f, "NetworkAddress::PointerAddress({addr})")
281            }
282        }
283    }
284}
285
286/// Pretty print a `kad::KBucketKey` as a hex string.
287#[derive(Clone)]
288pub struct PrettyPrintKBucketKey(pub Key<Vec<u8>>);
289
290impl std::fmt::Display for PrettyPrintKBucketKey {
291    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
292        for byte in self.0.hashed_bytes() {
293            f.write_fmt(format_args!("{byte:02x}"))?;
294        }
295        Ok(())
296    }
297}
298
299impl std::fmt::Debug for PrettyPrintKBucketKey {
300    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
301        write!(f, "{self}")
302    }
303}
304
305/// Provides a hex representation of a `kad::RecordKey`.
306///
307/// This internally stores the RecordKey as a `Cow` type. Use `PrettyPrintRecordKey::from(&RecordKey)` to create a
308/// borrowed version for printing/logging.
309/// To use in error messages, to pass to other functions, call `PrettyPrintRecordKey::from(&RecordKey).into_owned()` to
310///  obtain a cloned, non-referenced `RecordKey`.
311#[derive(Clone, Hash, Eq, PartialEq)]
312pub struct PrettyPrintRecordKey<'a> {
313    key: Cow<'a, RecordKey>,
314}
315
316impl Serialize for PrettyPrintRecordKey<'_> {
317    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
318    where
319        S: Serializer,
320    {
321        let record_key_bytes = match &self.key {
322            Cow::Borrowed(borrowed_key) => borrowed_key.as_ref(),
323            Cow::Owned(owned_key) => owned_key.as_ref(),
324        };
325        record_key_bytes.serialize(serializer)
326    }
327}
328
329// Implementing Deserialize for PrettyPrintRecordKey
330impl<'de> Deserialize<'de> for PrettyPrintRecordKey<'static> {
331    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
332    where
333        D: Deserializer<'de>,
334    {
335        // Deserialize to bytes first
336        let bytes = Vec::<u8>::deserialize(deserializer)?;
337        // Then use the bytes to create a RecordKey and wrap it in PrettyPrintRecordKey
338        Ok(PrettyPrintRecordKey {
339            key: Cow::Owned(RecordKey::new(&bytes)),
340        })
341    }
342}
343/// This is the only interface to create a PrettyPrintRecordKey.
344/// `.into_owned()` must be called explicitly if you want a Owned version to be used for errors/args.
345impl<'a> From<&'a RecordKey> for PrettyPrintRecordKey<'a> {
346    fn from(key: &'a RecordKey) -> Self {
347        PrettyPrintRecordKey {
348            key: Cow::Borrowed(key),
349        }
350    }
351}
352
353impl PrettyPrintRecordKey<'_> {
354    /// Creates a owned version that can be then used to pass as error values.
355    /// Do not call this if you just want to print/log `PrettyPrintRecordKey`
356    pub fn into_owned(self) -> PrettyPrintRecordKey<'static> {
357        let cloned_key = match self.key {
358            Cow::Borrowed(key) => Cow::Owned(key.clone()),
359            Cow::Owned(key) => Cow::Owned(key),
360        };
361
362        PrettyPrintRecordKey { key: cloned_key }
363    }
364
365    pub fn no_kbucket_log(self) -> String {
366        let mut content = String::from("");
367        let record_key_bytes = match &self.key {
368            Cow::Borrowed(borrowed_key) => borrowed_key.as_ref(),
369            Cow::Owned(owned_key) => owned_key.as_ref(),
370        };
371        for byte in record_key_bytes {
372            let _ = content.write_fmt(format_args!("{byte:02x}"));
373        }
374        content
375    }
376}
377
378impl std::fmt::Display for PrettyPrintRecordKey<'_> {
379    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
380        let record_key_bytes = match &self.key {
381            Cow::Borrowed(borrowed_key) => borrowed_key.as_ref(),
382            Cow::Owned(owned_key) => owned_key.as_ref(),
383        };
384        // print the first 6 chars
385        for byte in record_key_bytes.iter().take(3) {
386            f.write_fmt(format_args!("{byte:02x}"))?;
387        }
388
389        write!(
390            f,
391            "({:?})",
392            PrettyPrintKBucketKey(NetworkAddress::from(self.key.as_ref()).as_kbucket_key())
393        )
394    }
395}
396
397impl std::fmt::Debug for PrettyPrintRecordKey<'_> {
398    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
399        // same as display
400        write!(f, "{self}")
401    }
402}
403
404#[cfg(test)]
405mod tests {
406    use crate::{
407        NetworkAddress, PeerId,
408        messages::{Nonce, Query},
409        storage::GraphEntryAddress,
410    };
411    use serde::{Deserialize, Serialize};
412
413    #[test]
414    fn verify_graph_entry_addr_is_actionable() {
415        let pk = bls::SecretKey::random().public_key();
416        let graph_entry_addr = GraphEntryAddress::new(pk);
417        let net_addr = NetworkAddress::from(graph_entry_addr);
418
419        let graph_entry_addr_hex = &graph_entry_addr.to_hex();
420        let net_addr_fmt = format!("{net_addr}");
421
422        assert!(net_addr_fmt.contains(graph_entry_addr_hex));
423    }
424
425    #[derive(Eq, PartialEq, PartialOrd, Clone, Serialize, Deserialize, Debug)]
426    enum QueryExtended {
427        GetStoreQuote {
428            key: NetworkAddress,
429            data_type: u32,
430            data_size: usize,
431            nonce: Option<Nonce>,
432            difficulty: usize,
433        },
434        GetReplicatedRecord {
435            requester: NetworkAddress,
436            key: NetworkAddress,
437        },
438        GetChunkExistenceProof {
439            key: NetworkAddress,
440            nonce: Nonce,
441            difficulty: usize,
442        },
443        CheckNodeInProblem(NetworkAddress),
444        GetClosestPeers {
445            key: NetworkAddress,
446            num_of_peers: Option<usize>,
447            range: Option<[u8; 32]>,
448            sign_result: bool,
449        },
450        GetVersion(NetworkAddress),
451        AdditionalVariant(NetworkAddress), // New variant for extended functionality
452    }
453
454    #[test]
455    fn test_query_serialization_deserialization() {
456        let peer_id = PeerId::random();
457        // Create a sample Query message
458        let original_query = Query::GetStoreQuote {
459            key: NetworkAddress::from(peer_id),
460            data_type: 1,
461            data_size: 100,
462            nonce: Some(0),
463            difficulty: 3,
464        };
465
466        // Serialize to bytes
467        let serialized = bincode::serialize(&original_query).expect("Serialization failed");
468
469        // Deserialize into QueryExtended
470        let deserialized: QueryExtended =
471            bincode::deserialize(&serialized).expect("Deserialization into QueryExtended failed");
472
473        // Verify the deserialized data matches the original
474        match deserialized {
475            QueryExtended::GetStoreQuote {
476                key,
477                data_type,
478                data_size,
479                nonce,
480                difficulty,
481            } => {
482                assert_eq!(key, NetworkAddress::from(peer_id));
483                assert_eq!(data_type, 1);
484                assert_eq!(data_size, 100);
485                assert_eq!(nonce, Some(0));
486                assert_eq!(difficulty, 3);
487            }
488            _ => panic!("Deserialized into wrong variant"),
489        }
490    }
491
492    #[test]
493    fn test_query_extended_serialization() {
494        // Create a sample QueryExtended message with extended new variant
495        let extended_query =
496            QueryExtended::AdditionalVariant(NetworkAddress::from(PeerId::random()));
497
498        // Serialize to bytes
499        let serialized = bincode::serialize(&extended_query).expect("Serialization failed");
500
501        // Attempt to deserialize into original Query (should fail)
502        let result: Result<Query, _> = bincode::deserialize(&serialized);
503        assert!(
504            result.is_err(),
505            "Should fail to deserialize extended enum into original"
506        );
507
508        let peer_id = PeerId::random();
509        // Create a sample QueryExtended message with old variant
510        let extended_query = QueryExtended::GetStoreQuote {
511            key: NetworkAddress::from(peer_id),
512            data_type: 1,
513            data_size: 100,
514            nonce: Some(0),
515            difficulty: 3,
516        };
517
518        // Serialize to bytes
519        let serialized = bincode::serialize(&extended_query).expect("Serialization failed");
520
521        // Deserialize into Query
522        let deserialized: Query =
523            bincode::deserialize(&serialized).expect("Deserialization into Query failed");
524
525        // Verify the deserialized data matches the original
526        match deserialized {
527            Query::GetStoreQuote {
528                key,
529                data_type,
530                data_size,
531                nonce,
532                difficulty,
533            } => {
534                assert_eq!(key, NetworkAddress::from(peer_id));
535                assert_eq!(data_type, 1);
536                assert_eq!(data_size, 100);
537                assert_eq!(nonce, Some(0));
538                assert_eq!(difficulty, 3);
539            }
540            _ => panic!("Deserialized into wrong variant"),
541        }
542    }
543}