1use 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#[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 #[error("Record not stored by nodes, it could be invalid, else you should retry: {0:?}")]
53 RecordNotStoredByNodes(NetworkAddress),
54
55 #[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 #[error("Failed to verify the ChunkProof with the provided quorum")]
67 FailedToVerifyChunkProof(NetworkAddress),
68
69 #[error("Graph entry not found: {0:?}")]
71 NoGraphEntryFoundInsideRecord(GraphEntryAddress),
72
73 #[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 #[error("Network GetClosest TimedOut")]
88 GetClosestTimedOut,
89
90 #[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 #[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
124pub 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
152pub 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
171fn 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]; 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}