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