ant_networking/
error.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
9use ant_protocol::storage::GraphEntryAddress;
10use ant_protocol::{messages::Response, storage::RecordKind, NetworkAddress};
11use libp2p::swarm::ListenError;
12use libp2p::{
13    kad::{self, QueryId},
14    request_response::{OutboundFailure, OutboundRequestId},
15    swarm::DialError,
16    TransportError,
17};
18use std::{collections::HashMap, fmt::Debug, io, path::PathBuf};
19use thiserror::Error;
20use tokio::sync::oneshot;
21use tracing::Level;
22
23const TRACING_ERROR_LEVEL: Level = Level::ERROR;
24
25pub(super) type Result<T, E = NetworkError> = std::result::Result<T, E>;
26
27/// Network Errors
28#[derive(Debug, Error)]
29pub enum NetworkError {
30    #[error("Dial Error")]
31    DialError(#[from] DialError),
32
33    #[error("I/O error: {0}")]
34    Io(#[from] io::Error),
35
36    #[error("Kademlia Store error: {0}")]
37    KademliaStoreError(#[from] kad::store::Error),
38
39    #[error("Transport Error")]
40    TransportError(#[from] TransportError<std::io::Error>),
41
42    #[error("SnProtocol Error: {0}")]
43    ProtocolError(#[from] ant_protocol::error::Error),
44
45    #[error("Evm payment Error {0}")]
46    EvmPaymemt(#[from] ant_evm::EvmError),
47
48    #[error("Failed to sign the message with the PeerId keypair")]
49    SigningFailed(#[from] libp2p::identity::SigningError),
50
51    // ---------- Record Errors
52    #[error("Record not stored by nodes, it could be invalid, else you should retry: {0:?}")]
53    RecordNotStoredByNodes(NetworkAddress),
54
55    // The RecordKind that was obtained did not match with the expected one
56    #[error("The RecordKind obtained from the Record did not match with the expected kind: {0}")]
57    RecordKindMismatch(RecordKind),
58
59    #[error("Record header is incorrect")]
60    InCorrectRecordHeader,
61
62    #[error("The operation is not allowed on a client record store")]
63    OperationNotAllowedOnClientRecordStore,
64
65    // ---------- Chunk Errors
66    #[error("Failed to verify the ChunkProof with the provided quorum")]
67    FailedToVerifyChunkProof(NetworkAddress),
68
69    // ---------- Graph Errors
70    #[error("Graph entry not found: {0:?}")]
71    NoGraphEntryFoundInsideRecord(GraphEntryAddress),
72
73    // ---------- Store Error
74    #[error("Not Enough Peers for Store Cost Request")]
75    NotEnoughPeersForStoreCostRequest,
76
77    #[error("No Store Cost Responses")]
78    NoStoreCostResponses,
79
80    #[error("Could not create storage dir: {path:?}, error: {source}")]
81    FailedToCreateRecordStoreDir {
82        path: PathBuf,
83        source: std::io::Error,
84    },
85
86    // ---------- Kad Network Errors
87    #[error("Network GetClosest TimedOut")]
88    GetClosestTimedOut,
89
90    // ---------- Internal Network Errors
91    #[error("Could not get enough peers ({required}) to satisfy the request, found {found}")]
92    NotEnoughPeers { found: usize, required: usize },
93
94    #[error("Node Listen Address was not provided during construction")]
95    ListenAddressNotProvided,
96
97    #[cfg(feature = "open-metrics")]
98    #[error("Network Metric error")]
99    NetworkMetricError,
100
101    // ---------- Channel Errors
102    #[error("Outbound Error")]
103    OutboundError(#[from] OutboundFailure),
104
105    #[error("A Kademlia event has been dropped: {query_id:?} {event}")]
106    ReceivedKademliaEventDropped { query_id: QueryId, event: String },
107
108    #[error("The oneshot::sender has been dropped")]
109    SenderDropped(#[from] oneshot::error::RecvError),
110
111    #[error("Internal messaging channel was dropped")]
112    InternalMsgChannelDropped,
113
114    #[error("Response received for a request not found in our local tracking map: {0}")]
115    ReceivedResponseDropped(OutboundRequestId),
116
117    #[error("Outgoing response has been dropped due to a conn being closed or timeout: {0}")]
118    OutgoingResponseDropped(Response),
119
120    #[error("Error setting up behaviour: {0}")]
121    BehaviourErr(String),
122}
123
124/// Return a list of error strings for the DialError type
125pub fn dial_error_to_str(err: &DialError) -> Vec<(String, Level)> {
126    match err {
127        DialError::LocalPeerId { .. } => {
128            vec![("DialError::LocalPeerId".to_string(), TRACING_ERROR_LEVEL)]
129        }
130        DialError::NoAddresses => vec![("DialError::NoAddresses".to_string(), TRACING_ERROR_LEVEL)],
131        DialError::DialPeerConditionFalse(peer_condition) => {
132            vec![(
133                format!("DialError::DialPeerConditionFalse::{peer_condition:?}"),
134                TRACING_ERROR_LEVEL,
135            )]
136        }
137        DialError::Aborted => vec![("DialError::Aborted".to_string(), TRACING_ERROR_LEVEL)],
138        DialError::WrongPeerId { .. } => {
139            vec![("DialError::WrongPeerId".to_string(), TRACING_ERROR_LEVEL)]
140        }
141        DialError::Denied { .. } => vec![("DialError::Denied".to_string(), TRACING_ERROR_LEVEL)],
142        DialError::Transport(items) => items
143            .iter()
144            .map(|(_, error)| {
145                let (error_str, level) = transport_err_to_str(error);
146                (format!("DialError::{error_str}"), level)
147            })
148            .collect(),
149    }
150}
151
152/// Return a string for the ListenError type
153pub fn listen_error_to_str(err: &ListenError) -> (String, Level) {
154    match err {
155        ListenError::Aborted => ("ListenError::Aborted".to_string(), TRACING_ERROR_LEVEL),
156
157        ListenError::WrongPeerId { .. } => {
158            ("ListenError::WrongPeerId".to_string(), TRACING_ERROR_LEVEL)
159        }
160        ListenError::LocalPeerId { .. } => {
161            ("ListenError::LocalPeerId".to_string(), TRACING_ERROR_LEVEL)
162        }
163        ListenError::Denied { .. } => ("ListenError::Denied".to_string(), TRACING_ERROR_LEVEL),
164        ListenError::Transport(transport_error) => {
165            let (error_str, level) = transport_err_to_str(transport_error);
166            (format!("ListenError::{error_str}"), level)
167        }
168    }
169}
170
171/// Return a string for the TransportError type
172fn transport_err_to_str(err: &TransportError<std::io::Error>) -> (String, Level) {
173    match err {
174        TransportError::MultiaddrNotSupported { .. } => (
175            "TransportError::MultiaddrNotSupported".to_string(),
176            Level::ERROR,
177        ),
178        TransportError::Other(err) => {
179            let some_known_errors = HashMap::from([
180                (
181                    "ConnectionRefused",
182                    ("ConnectionRefused", TRACING_ERROR_LEVEL),
183                ),
184                ("HostUnreachable", ("HostUnreachable", TRACING_ERROR_LEVEL)),
185                (
186                    "HandshakeTimedOut",
187                    ("HandshakeTimedOut", TRACING_ERROR_LEVEL),
188                ),
189                ("TimedOut", ("TimedOut", TRACING_ERROR_LEVEL)),
190                (
191                    "ResponseFromBehaviourCanceled",
192                    ("ResponseFromBehaviourCanceled", TRACING_ERROR_LEVEL),
193                ),
194                ("ConnectionLost", ("ConnectionLost", TRACING_ERROR_LEVEL)),
195                ("ConnectionClosed", ("ConnectionClosed", Level::DEBUG)),
196                (
197                    "ConnectionFailed",
198                    ("ConnectionFailed", TRACING_ERROR_LEVEL),
199                ),
200                (
201                    "MALFORMED_MESSAGE",
202                    ("MalformedMessage", TRACING_ERROR_LEVEL),
203                ),
204                ("UnexpectedEof", ("UnexpectedEof", TRACING_ERROR_LEVEL)),
205                ("Select(Failed)", ("Failed", TRACING_ERROR_LEVEL)),
206            ]);
207
208            let mut err_str = None;
209            for (err_substr, (err_display, tracing_level)) in some_known_errors.iter() {
210                if format!("{err:?}").contains(err_substr) {
211                    err_str = Some((format!("TransportError::{err_display}"), *tracing_level));
212                    break;
213                }
214            }
215
216            err_str.unwrap_or_else(|| ("TransportError::Other".to_string(), TRACING_ERROR_LEVEL))
217        }
218    }
219}
220
221#[cfg(test)]
222mod tests {
223    use ant_protocol::{
224        storage::ChunkAddress, NetworkAddress, PrettyPrintKBucketKey, PrettyPrintRecordKey,
225    };
226    use xor_name::XorName;
227
228    #[test]
229    fn test_client_sees_same_hex_in_errors_for_xorname_and_record_keys() {
230        let mut rng = rand::thread_rng();
231        let xor_name = XorName::random(&mut rng);
232        let address = ChunkAddress::new(xor_name);
233        let network_address = NetworkAddress::from(address);
234        let record_key = network_address.to_record_key();
235        let record_str = format!("{}", PrettyPrintRecordKey::from(&record_key));
236        let xor_name_str = &format!("{xor_name:64x}")[0..6]; // only the first 6 chars are logged
237        let xor_name_str = format!(
238            "{xor_name_str}({:?})",
239            PrettyPrintKBucketKey(network_address.as_kbucket_key())
240        );
241        println!("record_str: {record_str}");
242        println!("xor_name_str: {xor_name_str}");
243        assert_eq!(record_str, xor_name_str);
244    }
245}