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    NetworkAddress, PrettyPrintRecordKey,
17    storage::{DataTypes, ValidationType},
18};
19use libp2p::{
20    PeerId,
21    core::Multiaddr,
22    kad::{KBucketDistance as Distance, Record, RecordKey},
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>>>,
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    /// or record indexing cache is out-of-sync with disk files
107    RemoveFailedLocalRecord {
108        key: RecordKey,
109    },
110    /// Add a local record to the RecordStore's HashSet of stored records
111    /// This should be done after the record has been stored to disk
112    AddLocalRecordAsStored {
113        key: RecordKey,
114        record_type: ValidationType,
115        data_type: DataTypes,
116    },
117    /// Add a peer to the blocklist
118    AddPeerToBlockList {
119        peer_id: PeerId,
120    },
121    /// Notify whether peer is in trouble
122    RecordNodeIssue {
123        peer_id: PeerId,
124        issue: NodeIssue,
125    },
126    // Whether peer is considered as `in trouble` by self
127    IsPeerShunned {
128        target: NetworkAddress,
129        sender: oneshot::Sender<bool>,
130    },
131    // Quote verification agaisnt historical collected quotes
132    QuoteVerification {
133        quotes: Vec<(PeerId, PaymentQuote)>,
134    },
135    // Notify a fetch completion
136    FetchCompleted((RecordKey, ValidationType)),
137    /// Triggers interval repliation
138    /// NOTE: This does result in outgoing messages, but is produced locally
139    TriggerIntervalReplication,
140    /// Triggers unrelevant record cleanup
141    TriggerIrrelevantRecordCleanup,
142    /// Send peer scores (collected from storage challenge) to replication_fetcher
143    NotifyPeerScores {
144        peer_scores: Vec<(PeerId, bool)>,
145    },
146    /// Add fresh replicate records into replication_fetcher
147    AddFreshReplicateRecords {
148        holder: NetworkAddress,
149        keys: Vec<(NetworkAddress, ValidationType)>,
150    },
151    /// Notify a fetched peer's version
152    NotifyPeerVersion {
153        peer: PeerId,
154        version: String,
155    },
156    /// Get responsible distance range.
157    GetNetworkDensity {
158        sender: oneshot::Sender<Option<Distance>>,
159    },
160    /// Remove peer from the routing table
161    RemovePeer {
162        peer: PeerId,
163    },
164    /// Some records were not found at their target location
165    RecordNotAtTargetLocation,
166}
167
168/// Debug impl for LocalSwarmCmd to avoid printing full Record, instead only RecodKey
169/// and RecordKind are printed.
170impl Debug for LocalSwarmCmd {
171    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172        match self {
173            LocalSwarmCmd::PutLocalRecord {
174                record,
175                is_client_put,
176            } => {
177                write!(
178                    f,
179                    "LocalSwarmCmd::PutLocalRecord {{ key: {:?}, is_client_put: {is_client_put:?} }}",
180                    PrettyPrintRecordKey::from(&record.key)
181                )
182            }
183            LocalSwarmCmd::RemoveFailedLocalRecord { key } => {
184                write!(
185                    f,
186                    "LocalSwarmCmd::RemoveFailedLocalRecord {{ key: {:?} }}",
187                    PrettyPrintRecordKey::from(key)
188                )
189            }
190            LocalSwarmCmd::AddLocalRecordAsStored {
191                key,
192                record_type,
193                data_type,
194            } => {
195                write!(
196                    f,
197                    "LocalSwarmCmd::AddLocalRecordAsStored {{ key: {:?}, record_type: {record_type:?}, data_type: {data_type:?} }}",
198                    PrettyPrintRecordKey::from(key)
199                )
200            }
201            LocalSwarmCmd::GetKCloseLocalPeersToTarget { key, .. } => {
202                write!(
203                    f,
204                    "LocalSwarmCmd::GetKCloseLocalPeersToTarget {{ key: {key:?} }}"
205                )
206            }
207            LocalSwarmCmd::GetLocalQuotingMetrics { .. } => {
208                write!(f, "LocalSwarmCmd::GetLocalQuotingMetrics")
209            }
210            LocalSwarmCmd::PaymentReceived => {
211                write!(f, "LocalSwarmCmd::PaymentReceived")
212            }
213            LocalSwarmCmd::GetLocalRecord { key, .. } => {
214                write!(
215                    f,
216                    "LocalSwarmCmd::GetLocalRecord {{ key: {:?} }}",
217                    PrettyPrintRecordKey::from(key)
218                )
219            }
220            LocalSwarmCmd::GetAllLocalRecordAddresses { .. } => {
221                write!(f, "LocalSwarmCmd::GetAllLocalRecordAddresses")
222            }
223            LocalSwarmCmd::GetPeersWithMultiaddr { .. } => {
224                write!(f, "LocalSwarmCmd::GetPeersWithMultiaddr")
225            }
226            LocalSwarmCmd::GetKBuckets { .. } => {
227                write!(f, "LocalSwarmCmd::GetKBuckets")
228            }
229            LocalSwarmCmd::GetSwarmLocalState { .. } => {
230                write!(f, "LocalSwarmCmd::GetSwarmLocalState")
231            }
232            LocalSwarmCmd::RecordStoreHasKey { key, .. } => {
233                write!(
234                    f,
235                    "LocalSwarmCmd::RecordStoreHasKey {:?}",
236                    PrettyPrintRecordKey::from(key)
237                )
238            }
239            LocalSwarmCmd::AddPeerToBlockList { peer_id } => {
240                write!(f, "LocalSwarmCmd::AddPeerToBlockList {peer_id:?}")
241            }
242            LocalSwarmCmd::RecordNodeIssue { peer_id, issue } => {
243                write!(
244                    f,
245                    "LocalSwarmCmd::SendNodeStatus peer {peer_id:?}, issue: {issue:?}"
246                )
247            }
248            LocalSwarmCmd::IsPeerShunned { target, .. } => {
249                write!(f, "LocalSwarmCmd::IsPeerInTrouble target: {target:?}")
250            }
251            LocalSwarmCmd::QuoteVerification { quotes } => {
252                write!(
253                    f,
254                    "LocalSwarmCmd::QuoteVerification of {} quotes",
255                    quotes.len()
256                )
257            }
258            LocalSwarmCmd::FetchCompleted((key, record_type)) => {
259                write!(
260                    f,
261                    "LocalSwarmCmd::FetchCompleted({record_type:?} : {:?})",
262                    PrettyPrintRecordKey::from(key)
263                )
264            }
265            LocalSwarmCmd::TriggerIntervalReplication => {
266                write!(f, "LocalSwarmCmd::TriggerIntervalReplication")
267            }
268            LocalSwarmCmd::TriggerIrrelevantRecordCleanup => {
269                write!(f, "LocalSwarmCmd::TriggerUnrelevantRecordCleanup")
270            }
271            LocalSwarmCmd::NotifyPeerScores { peer_scores } => {
272                write!(f, "LocalSwarmCmd::NotifyPeerScores({peer_scores:?})")
273            }
274            LocalSwarmCmd::AddFreshReplicateRecords { holder, keys } => {
275                write!(
276                    f,
277                    "LocalSwarmCmd::AddFreshReplicateRecords({holder:?}, {keys:?})"
278                )
279            }
280            LocalSwarmCmd::NotifyPeerVersion { peer, version } => {
281                write!(f, "LocalSwarmCmd::NotifyPeerVersion({peer:?}, {version:?})")
282            }
283            LocalSwarmCmd::GetNetworkDensity { .. } => {
284                write!(f, "LocalSwarmCmd::GetNetworkDensity")
285            }
286            LocalSwarmCmd::RemovePeer { peer } => {
287                write!(f, "LocalSwarmCmd::RemovePeer({peer:?})")
288            }
289            LocalSwarmCmd::RecordNotAtTargetLocation => {
290                write!(f, "LocalSwarmCmd::RecordNotAtTargetLocation")
291            }
292        }
293    }
294}
295
296/// Snapshot of information kept in the Swarm's local state
297#[derive(Debug, Clone)]
298pub struct SwarmLocalState {
299    /// List of peers that we have an established connection with.
300    pub connected_peers: Vec<PeerId>,
301    /// The number of peers in the routing table
302    pub peers_in_routing_table: usize,
303    /// List of addresses the node is currently listening on
304    pub listeners: Vec<Multiaddr>,
305}