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