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