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, PrettyPrintRecordKey};
11use libp2p::{
12    kad::{self, QueryId, Record},
13    request_response::{OutboundFailure, OutboundRequestId},
14    swarm::DialError,
15    PeerId, TransportError,
16};
17use std::{
18    collections::{HashMap, HashSet},
19    fmt::Debug,
20    io,
21    path::PathBuf,
22};
23use thiserror::Error;
24use tokio::sync::oneshot;
25use xor_name::XorName;
26
27pub(super) type Result<T, E = NetworkError> = std::result::Result<T, E>;
28
29/// GetRecord Query errors
30#[derive(Error, Clone)]
31pub enum GetRecordError {
32    #[error("Get Record completed with non enough copies")]
33    NotEnoughCopies {
34        record: Record,
35        expected: usize,
36        got: usize,
37    },
38    #[error("Network query timed out")]
39    QueryTimeout,
40    #[error("Record retrieved from the network does not match the provided target record.")]
41    RecordDoesNotMatch(Record),
42    #[error("The record kind for the split records did not match")]
43    RecordKindMismatch,
44    #[error("Record not found in the network")]
45    RecordNotFound,
46    // Avoid logging the whole `Record` content by accident.
47    /// The split record error will be handled at the network layer.
48    /// For GraphEntry, it accumulates them
49    #[error("Split Record has {} different copies", result_map.len())]
50    SplitRecord {
51        result_map: HashMap<XorName, (Record, HashSet<PeerId>)>,
52    },
53}
54
55impl Debug for GetRecordError {
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        match self {
58            Self::NotEnoughCopies {
59                record,
60                expected,
61                got,
62            } => {
63                let pretty_key = PrettyPrintRecordKey::from(&record.key);
64                f.debug_struct("NotEnoughCopies")
65                    .field("record_key", &pretty_key)
66                    .field("expected", &expected)
67                    .field("got", &got)
68                    .finish()
69            }
70            Self::QueryTimeout => write!(f, "QueryTimeout"),
71            Self::RecordDoesNotMatch(record) => {
72                let pretty_key = PrettyPrintRecordKey::from(&record.key);
73                f.debug_tuple("RecordDoesNotMatch")
74                    .field(&pretty_key)
75                    .finish()
76            }
77            Self::RecordKindMismatch => write!(f, "RecordKindMismatch"),
78            Self::RecordNotFound => write!(f, "RecordNotFound"),
79            Self::SplitRecord { result_map } => f
80                .debug_struct("SplitRecord")
81                .field("result_map_count", &result_map.len())
82                .finish(),
83        }
84    }
85}
86
87/// Network Errors
88#[derive(Debug, Error)]
89pub enum NetworkError {
90    #[error("Dial Error")]
91    DialError(#[from] DialError),
92
93    #[error("I/O error: {0}")]
94    Io(#[from] io::Error),
95
96    #[error("Kademlia Store error: {0}")]
97    KademliaStoreError(#[from] kad::store::Error),
98
99    #[error("Transport Error")]
100    TransportError(#[from] TransportError<std::io::Error>),
101
102    #[error("SnProtocol Error: {0}")]
103    ProtocolError(#[from] ant_protocol::error::Error),
104
105    #[error("Evm payment Error {0}")]
106    EvmPaymemt(#[from] ant_evm::EvmError),
107
108    #[error("Failed to sign the message with the PeerId keypair")]
109    SigningFailed(#[from] libp2p::identity::SigningError),
110
111    // ---------- Record Errors
112    // GetRecord query errors
113    #[error("GetRecord Query Error {0:?}")]
114    GetRecordError(#[from] GetRecordError),
115    #[error("Record not stored by nodes, it could be invalid, else you should retry: {0:?}")]
116    RecordNotStoredByNodes(NetworkAddress),
117
118    // The RecordKind that was obtained did not match with the expected one
119    #[error("The RecordKind obtained from the Record did not match with the expected kind: {0}")]
120    RecordKindMismatch(RecordKind),
121
122    #[error("Record header is incorrect")]
123    InCorrectRecordHeader,
124
125    #[error("The operation is not allowed on a client record store")]
126    OperationNotAllowedOnClientRecordStore,
127
128    // ---------- Chunk Errors
129    #[error("Failed to verify the ChunkProof with the provided quorum")]
130    FailedToVerifyChunkProof(NetworkAddress),
131
132    // ---------- Graph Errors
133    #[error("Graph entry not found: {0:?}")]
134    NoGraphEntryFoundInsideRecord(GraphEntryAddress),
135
136    // ---------- Store Error
137    #[error("Not Enough Peers for Store Cost Request")]
138    NotEnoughPeersForStoreCostRequest,
139
140    #[error("No Store Cost Responses")]
141    NoStoreCostResponses,
142
143    #[error("Could not create storage dir: {path:?}, error: {source}")]
144    FailedToCreateRecordStoreDir {
145        path: PathBuf,
146        source: std::io::Error,
147    },
148
149    // ---------- Kad Network Errors
150    #[error("Network GetClosest TimedOut")]
151    GetClosestTimedOut,
152
153    // ---------- Internal Network Errors
154    #[error("Could not get enough peers ({required}) to satisfy the request, found {found}")]
155    NotEnoughPeers { found: usize, required: usize },
156
157    #[error("Node Listen Address was not provided during construction")]
158    ListenAddressNotProvided,
159
160    #[cfg(feature = "open-metrics")]
161    #[error("Network Metric error")]
162    NetworkMetricError,
163
164    // ---------- Channel Errors
165    #[error("Outbound Error")]
166    OutboundError(#[from] OutboundFailure),
167
168    #[error("A Kademlia event has been dropped: {query_id:?} {event}")]
169    ReceivedKademliaEventDropped { query_id: QueryId, event: String },
170
171    #[error("The oneshot::sender has been dropped")]
172    SenderDropped(#[from] oneshot::error::RecvError),
173
174    #[error("Internal messaging channel was dropped")]
175    InternalMsgChannelDropped,
176
177    #[error("Response received for a request not found in our local tracking map: {0}")]
178    ReceivedResponseDropped(OutboundRequestId),
179
180    #[error("Outgoing response has been dropped due to a conn being closed or timeout: {0}")]
181    OutgoingResponseDropped(Response),
182
183    #[error("Error setting up behaviour: {0}")]
184    BehaviourErr(String),
185}
186
187#[cfg(test)]
188mod tests {
189    use ant_protocol::{storage::ChunkAddress, NetworkAddress, PrettyPrintKBucketKey};
190    use xor_name::XorName;
191
192    use super::*;
193
194    #[test]
195    fn test_client_sees_same_hex_in_errors_for_xorname_and_record_keys() {
196        let mut rng = rand::thread_rng();
197        let xor_name = XorName::random(&mut rng);
198        let address = ChunkAddress::new(xor_name);
199        let network_address = NetworkAddress::from(address);
200        let record_key = network_address.to_record_key();
201        let record_str = format!("{}", PrettyPrintRecordKey::from(&record_key));
202        let xor_name_str = &format!("{xor_name:64x}")[0..6]; // only the first 6 chars are logged
203        let xor_name_str = format!(
204            "{xor_name_str}({:?})",
205            PrettyPrintKBucketKey(network_address.as_kbucket_key())
206        );
207        println!("record_str: {record_str}");
208        println!("xor_name_str: {xor_name_str}");
209        assert_eq!(record_str, xor_name_str);
210    }
211}