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