ant_node/networking/interface/
local_cmd.rs

1// Copyright 2025 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::{
10    collections::{BTreeMap, HashMap},
11    fmt::Debug,
12};
13
14use ant_evm::{PaymentQuote, QuotingMetrics};
15use ant_protocol::{
16    storage::{DataTypes, ValidationType},
17    NetworkAddress, PrettyPrintRecordKey,
18};
19use libp2p::{
20    core::Multiaddr,
21    kad::{KBucketDistance as Distance, Record, RecordKey},
22    PeerId,
23};
24use tokio::sync::oneshot;
25
26use crate::networking::Addresses;
27
28#[derive(Debug, Eq, PartialEq, Clone)]
29pub(crate) enum NodeIssue {
30    /// Some connections might be considered to be critical and should be tracked.
31    ConnectionIssue,
32    /// Peer responded with an unexpected PeerId
33    WrongPeerId,
34    /// Data Replication failed
35    ReplicationFailure,
36    /// Close nodes have reported this peer as bad
37    #[allow(dead_code)]
38    CloseNodesShunning,
39    /// Provided a bad quote
40    BadQuoting,
41    /// Peer failed to pass the chunk proof verification
42    FailedChunkProofCheck,
43}
44
45impl std::fmt::Display for NodeIssue {
46    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
47        match self {
48            NodeIssue::ConnectionIssue => write!(f, "CriticalConnectionIssue"),
49            NodeIssue::WrongPeerId => write!(f, "WrongPeerId"),
50            NodeIssue::ReplicationFailure => write!(f, "ReplicationFailure"),
51            NodeIssue::CloseNodesShunning => write!(f, "CloseNodesShunning"),
52            NodeIssue::BadQuoting => write!(f, "BadQuoting"),
53            NodeIssue::FailedChunkProofCheck => write!(f, "FailedChunkProofCheck"),
54        }
55    }
56}
57
58/// Commands to send to the Swarm
59pub(crate) enum LocalSwarmCmd {
60    /// Get a list of all peers in local RT, with correspondent Multiaddr info attached as well.
61    GetPeersWithMultiaddr {
62        sender: oneshot::Sender<Vec<(PeerId, Vec<Multiaddr>)>>,
63    },
64    /// Get a map where each key is the ilog2 distance of that Kbucket
65    /// and each value is a vector of peers in that bucket.
66    GetKBuckets {
67        sender: oneshot::Sender<(BTreeMap<u32, Vec<PeerId>>, usize)>,
68    },
69    // Get K closest peers to target from the local RoutingTable, self is included
70    GetKCloseLocalPeersToTarget {
71        key: NetworkAddress,
72        sender: oneshot::Sender<Vec<(PeerId, Addresses)>>,
73    },
74    GetSwarmLocalState(oneshot::Sender<SwarmLocalState>),
75    /// Check if the local RecordStore contains the provided key
76    RecordStoreHasKey {
77        key: RecordKey,
78        sender: oneshot::Sender<bool>,
79    },
80    /// Get the Addresses of all the Records held locally
81    GetAllLocalRecordAddresses {
82        sender: oneshot::Sender<HashMap<NetworkAddress, ValidationType>>,
83    },
84    /// Get data from the local RecordStore
85    GetLocalRecord {
86        key: RecordKey,
87        sender: oneshot::Sender<Option<Record>>,
88    },
89    /// GetLocalQuotingMetrics for this node
90    /// Returns the quoting metrics and whether the record at `key` is already stored locally
91    GetLocalQuotingMetrics {
92        key: RecordKey,
93        data_type: u32,
94        data_size: usize,
95        sender: oneshot::Sender<(QuotingMetrics, bool)>,
96    },
97    /// Notify the node received a payment.
98    PaymentReceived,
99    /// Put record to the local RecordStore
100    PutLocalRecord {
101        record: Record,
102        is_client_put: bool,
103    },
104    /// Remove a local record from the RecordStore
105    /// Typically because the disk write failure
106    RemoveFailedLocalRecord {
107        key: RecordKey,
108    },
109    /// Remove an entry from the RecordStore record indexing cache
110    /// Typically because record indexing cache is out-of-sync with disk files
111    RemoveOutOfSyncEntry {
112        key: RecordKey,
113    },
114    /// Add a local record to the RecordStore's HashSet of stored records
115    /// This should be done after the record has been stored to disk
116    AddLocalRecordAsStored {
117        key: RecordKey,
118        record_type: ValidationType,
119        data_type: DataTypes,
120    },
121    /// Add a peer to the blocklist
122    AddPeerToBlockList {
123        peer_id: PeerId,
124    },
125    /// Notify whether peer is in trouble
126    RecordNodeIssue {
127        peer_id: PeerId,
128        issue: NodeIssue,
129    },
130    // Whether peer is considered as `in trouble` by self
131    IsPeerShunned {
132        target: NetworkAddress,
133        sender: oneshot::Sender<bool>,
134    },
135    // Quote verification agaisnt historical collected quotes
136    QuoteVerification {
137        quotes: Vec<(PeerId, PaymentQuote)>,
138    },
139    // Notify a fetch completion
140    FetchCompleted((RecordKey, ValidationType)),
141    /// Triggers interval repliation
142    /// NOTE: This does result in outgoing messages, but is produced locally
143    TriggerIntervalReplication,
144    /// Triggers unrelevant record cleanup
145    TriggerIrrelevantRecordCleanup,
146    /// Send peer scores (collected from storage challenge) to replication_fetcher
147    NotifyPeerScores {
148        peer_scores: Vec<(PeerId, bool)>,
149    },
150    /// Add fresh replicate records into replication_fetcher
151    AddFreshReplicateRecords {
152        holder: NetworkAddress,
153        keys: Vec<(NetworkAddress, ValidationType)>,
154    },
155    /// Notify a fetched peer's version
156    NotifyPeerVersion {
157        peer: PeerId,
158        version: String,
159    },
160    /// Get responsible distance range.
161    GetNetworkDensity {
162        sender: oneshot::Sender<Option<Distance>>,
163    },
164    /// Remove peer from the routing table
165    RemovePeer {
166        peer: PeerId,
167    },
168    /// Some records were not found at their target location
169    RecordNotAtTargetLocation,
170}
171
172/// Debug impl for LocalSwarmCmd to avoid printing full Record, instead only RecodKey
173/// and RecordKind are printed.
174impl Debug for LocalSwarmCmd {
175    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176        match self {
177            LocalSwarmCmd::PutLocalRecord {
178                record,
179                is_client_put,
180            } => {
181                write!(
182                    f,
183                    "LocalSwarmCmd::PutLocalRecord {{ key: {:?}, is_client_put: {is_client_put:?} }}",
184                    PrettyPrintRecordKey::from(&record.key)
185                )
186            }
187            LocalSwarmCmd::RemoveFailedLocalRecord { key } => {
188                write!(
189                    f,
190                    "LocalSwarmCmd::RemoveFailedLocalRecord {{ key: {:?} }}",
191                    PrettyPrintRecordKey::from(key)
192                )
193            }
194            LocalSwarmCmd::RemoveOutOfSyncEntry { key } => {
195                write!(
196                    f,
197                    "LocalSwarmCmd::RemoveOutOfSyncEntry {{ key: {:?} }}",
198                    PrettyPrintRecordKey::from(key)
199                )
200            }
201            LocalSwarmCmd::AddLocalRecordAsStored {
202                key,
203                record_type,
204                data_type,
205            } => {
206                write!(
207                    f,
208                    "LocalSwarmCmd::AddLocalRecordAsStored {{ key: {:?}, record_type: {record_type:?}, data_type: {data_type:?} }}",
209                    PrettyPrintRecordKey::from(key)
210                )
211            }
212            LocalSwarmCmd::GetKCloseLocalPeersToTarget { key, .. } => {
213                write!(
214                    f,
215                    "LocalSwarmCmd::GetKCloseLocalPeersToTarget {{ key: {key:?} }}"
216                )
217            }
218            LocalSwarmCmd::GetLocalQuotingMetrics { .. } => {
219                write!(f, "LocalSwarmCmd::GetLocalQuotingMetrics")
220            }
221            LocalSwarmCmd::PaymentReceived => {
222                write!(f, "LocalSwarmCmd::PaymentReceived")
223            }
224            LocalSwarmCmd::GetLocalRecord { key, .. } => {
225                write!(
226                    f,
227                    "LocalSwarmCmd::GetLocalRecord {{ key: {:?} }}",
228                    PrettyPrintRecordKey::from(key)
229                )
230            }
231            LocalSwarmCmd::GetAllLocalRecordAddresses { .. } => {
232                write!(f, "LocalSwarmCmd::GetAllLocalRecordAddresses")
233            }
234            LocalSwarmCmd::GetPeersWithMultiaddr { .. } => {
235                write!(f, "LocalSwarmCmd::GetPeersWithMultiaddr")
236            }
237            LocalSwarmCmd::GetKBuckets { .. } => {
238                write!(f, "LocalSwarmCmd::GetKBuckets")
239            }
240            LocalSwarmCmd::GetSwarmLocalState { .. } => {
241                write!(f, "LocalSwarmCmd::GetSwarmLocalState")
242            }
243            LocalSwarmCmd::RecordStoreHasKey { key, .. } => {
244                write!(
245                    f,
246                    "LocalSwarmCmd::RecordStoreHasKey {:?}",
247                    PrettyPrintRecordKey::from(key)
248                )
249            }
250            LocalSwarmCmd::AddPeerToBlockList { peer_id } => {
251                write!(f, "LocalSwarmCmd::AddPeerToBlockList {peer_id:?}")
252            }
253            LocalSwarmCmd::RecordNodeIssue { peer_id, issue } => {
254                write!(
255                    f,
256                    "LocalSwarmCmd::SendNodeStatus peer {peer_id:?}, issue: {issue:?}"
257                )
258            }
259            LocalSwarmCmd::IsPeerShunned { target, .. } => {
260                write!(f, "LocalSwarmCmd::IsPeerInTrouble target: {target:?}")
261            }
262            LocalSwarmCmd::QuoteVerification { quotes } => {
263                write!(
264                    f,
265                    "LocalSwarmCmd::QuoteVerification of {} quotes",
266                    quotes.len()
267                )
268            }
269            LocalSwarmCmd::FetchCompleted((key, record_type)) => {
270                write!(
271                    f,
272                    "LocalSwarmCmd::FetchCompleted({record_type:?} : {:?})",
273                    PrettyPrintRecordKey::from(key)
274                )
275            }
276            LocalSwarmCmd::TriggerIntervalReplication => {
277                write!(f, "LocalSwarmCmd::TriggerIntervalReplication")
278            }
279            LocalSwarmCmd::TriggerIrrelevantRecordCleanup => {
280                write!(f, "LocalSwarmCmd::TriggerUnrelevantRecordCleanup")
281            }
282            LocalSwarmCmd::NotifyPeerScores { peer_scores } => {
283                write!(f, "LocalSwarmCmd::NotifyPeerScores({peer_scores:?})")
284            }
285            LocalSwarmCmd::AddFreshReplicateRecords { holder, keys } => {
286                write!(
287                    f,
288                    "LocalSwarmCmd::AddFreshReplicateRecords({holder:?}, {keys:?})"
289                )
290            }
291            LocalSwarmCmd::NotifyPeerVersion { peer, version } => {
292                write!(f, "LocalSwarmCmd::NotifyPeerVersion({peer:?}, {version:?})")
293            }
294            LocalSwarmCmd::GetNetworkDensity { .. } => {
295                write!(f, "LocalSwarmCmd::GetNetworkDensity")
296            }
297            LocalSwarmCmd::RemovePeer { peer } => {
298                write!(f, "LocalSwarmCmd::RemovePeer({peer:?})")
299            }
300            LocalSwarmCmd::RecordNotAtTargetLocation => {
301                write!(f, "LocalSwarmCmd::RecordNotAtTargetLocation")
302            }
303        }
304    }
305}
306
307/// Snapshot of information kept in the Swarm's local state
308#[derive(Debug, Clone)]
309pub struct SwarmLocalState {
310    /// List of peers that we have an established connection with.
311    pub connected_peers: Vec<PeerId>,
312    /// The number of peers in the routing table
313    pub peers_in_routing_table: usize,
314    /// List of addresses the node is currently listening on
315    pub listeners: Vec<Multiaddr>,
316}