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