ant_protocol/
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 crate::{NetworkAddress, PrettyPrintRecordKey};
10use libp2p::kad::store;
11use serde::{Deserialize, Serialize};
12use thiserror::Error;
13
14/// A specialised `Result` type for protocol crate.
15pub type Result<T> = std::result::Result<T, Error>;
16
17/// Main error types for the SAFE protocol.
18//
19// IMPORTANT DEV NOTE: when adding new variants to our Protocol Error enum,
20// make sure to keep them simple variants and not complex ones to keep retro compatibility.
21// this is a simple variant:  OK
22//    `NewErrorVariant`
23// this is a complex variant: NOT OK
24//    `NewErrorVariant( some other data type )`
25//
26// This test test_error_retro_compatibility_complex_types demonstrates the issue with complex types.
27//
28#[derive(Error, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
29#[non_exhaustive]
30pub enum Error {
31    // ---------- Chunk Proof errors
32    #[error("Chunk does not exist {0:?}")]
33    ChunkDoesNotExist(NetworkAddress),
34
35    // ---------- Scratchpad errors
36    /// The provided SecretyKey failed to decrypt the data
37    #[error("Failed to derive CipherText from encrypted_data")]
38    ScratchpadCipherTextFailed,
39    /// The provided cypher text is invalid
40    #[error("Provided cypher text is invalid")]
41    ScratchpadCipherTextInvalid,
42
43    // ---------- payment errors
44    #[error("There was an error getting the storecost from kademlia store")]
45    GetStoreQuoteFailed,
46    #[error("There was an error generating the payment quote")]
47    QuoteGenerationFailed,
48
49    // ---------- replication errors
50    /// Replication not found.
51    #[error("Peer {holder:?} cannot find Record {key:?}")]
52    ReplicatedRecordNotFound {
53        /// Holder that being contacted
54        holder: Box<NetworkAddress>,
55        /// Key of the missing record
56        key: Box<NetworkAddress>,
57    },
58
59    // ---------- record errors
60    // Could not Serialize/Deserialize RecordHeader from Record
61    #[error("Could not Serialize/Deserialize RecordHeader to/from Record")]
62    RecordHeaderParsingFailed,
63    // Could not Serialize/Deserialize Record
64    #[error("Could not Serialize/Deserialize Record")]
65    RecordParsingFailed,
66    // The record already exists at this node
67    #[error("The record already exists, so do not charge for it: {0:?}")]
68    RecordExists(PrettyPrintRecordKey<'static>),
69
70    // ---------- Record Put errors
71    #[error("Error handling record put: {0}")]
72    PutRecordFailed(String),
73    #[error("Outdated record: with counter {counter}, expected any above {expected}")]
74    OutdatedRecordCounter { counter: u64, expected: u64 },
75
76    // ---------- Merkle payment errors
77    #[error("There was an error getting the Merkle candidate quote: {0}")]
78    GetMerkleCandidateQuoteFailed(String),
79    #[error("Failed to sign Merkle candidate node: {0}")]
80    FailedToSignMerkleCandidate(String),
81    #[error("Merkle payment verification failed: {0}")]
82    MerklePaymentVerificationFailed(String),
83    #[error(
84        "Topology verification failed: only {valid_count}/{total_paid} paid nodes in closest {closest_count}"
85    )]
86    TopologyVerificationFailed {
87        /// Target address for distance calculations (reward pool midpoint)
88        target_address: Box<NetworkAddress>,
89        /// Number of paid nodes that were in the node's closest peers
90        valid_count: usize,
91        /// Total number of nodes that were paid
92        total_paid: usize,
93        /// Number of closest peers the node has
94        closest_count: usize,
95        /// The node's view of closest peers to the target
96        #[serde(with = "crate::peer_id_serde")]
97        node_peers: Vec<libp2p::PeerId>,
98        /// The peers that were paid (client's view)
99        #[serde(with = "crate::peer_id_serde")]
100        paid_peers: Vec<libp2p::PeerId>,
101    },
102
103    // Dev Note: add new variants above this one for backward compatibility with older protocol versions
104    // ---------- Unknown/fallback variant for retro compatibility
105    /// Unknown error variant (for backward compatibility with newer protocol versions)
106    #[error("Unknown error: the peer and you are using different protocol versions")]
107    #[serde(other)]
108    Unknown,
109}
110
111impl From<Error> for store::Error {
112    fn from(_err: Error) -> Self {
113        store::Error::ValueTooLarge
114    }
115}
116
117impl From<store::Error> for Error {
118    fn from(_err: store::Error) -> Self {
119        Error::RecordParsingFailed
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use libp2p::PeerId;
126
127    use super::*;
128
129    #[test]
130    fn test_error_retro_compatibility() {
131        // Test with a new struct that has a new variant
132        #[derive(Error, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
133        #[non_exhaustive]
134        enum ExtendedError {
135            #[error("Chunk does not exist {0:?}")]
136            ChunkDoesNotExist(NetworkAddress),
137            #[error("Failed to deserialize hex ScratchpadAddress")]
138            ScratchpadHexDeserializeFailed,
139            #[error("Failed to derive CipherText from encrypted_data")]
140            ScratchpadCipherTextFailed,
141            #[error("Provided cypher text is invalid")]
142            ScratchpadCipherTextInvalid,
143            #[error("There was an error getting the storecost from kademlia store")]
144            GetStoreQuoteFailed,
145            #[error("There was an error generating the payment quote")]
146            QuoteGenerationFailed,
147            #[error("Peer {holder:?} cannot find Record {key:?}")]
148            ReplicatedRecordNotFound {
149                holder: Box<NetworkAddress>,
150                key: Box<NetworkAddress>,
151            },
152            #[error("Could not Serialize/Deserialize RecordHeader to/from Record")]
153            RecordHeaderParsingFailed,
154            #[error("Could not Serialize/Deserialize Record")]
155            RecordParsingFailed,
156            #[error("The record already exists, so do not charge for it: {0:?}")]
157            RecordExists(PrettyPrintRecordKey<'static>),
158            // New variant that doesn't exist in the original Error enum
159            #[error("New error variant for testing")]
160            NewErrorVariant,
161            #[error("Unknown error variant")]
162            #[serde(other)]
163            Unknown,
164        }
165
166        // Test serialization and deserialization of ExtendedError
167        let extended_error = ExtendedError::NewErrorVariant;
168        let serialized = rmp_serde::to_vec(&extended_error).unwrap();
169
170        // Test that we can deserialize into the current Error enum
171        let deserialized: Error = rmp_serde::from_slice(&serialized).unwrap();
172        assert_eq!(deserialized, Error::Unknown);
173    }
174
175    #[test]
176    fn test_error_retro_compatibility_reduced() {
177        // Test with a struct that has a missing variant (simulating older version)
178        #[derive(Error, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
179        #[non_exhaustive]
180        enum ReducedError {
181            #[error("Chunk does not exist {0:?}")]
182            ChunkDoesNotExist(NetworkAddress),
183            #[error("Failed to deserialize hex ScratchpadAddress")]
184            ScratchpadHexDeserializeFailed,
185            // removed this variant
186            // - #[error("Failed to derive CipherText from encrypted_data")]
187            // - ScratchpadCipherTextFailed,
188            #[error("Provided cypher text is invalid")]
189            ScratchpadCipherTextInvalid,
190            #[error("There was an error getting the storecost from kademlia store")]
191            GetStoreQuoteFailed,
192            #[error("There was an error generating the payment quote")]
193            QuoteGenerationFailed,
194            #[error("Peer {holder:?} cannot find Record {key:?}")]
195            ReplicatedRecordNotFound {
196                holder: Box<NetworkAddress>,
197                key: Box<NetworkAddress>,
198            },
199            #[error("Could not Serialize/Deserialize RecordHeader to/from Record")]
200            RecordHeaderParsingFailed,
201            #[error("Could not Serialize/Deserialize Record")]
202            RecordParsingFailed,
203            #[error("The record already exists, so do not charge for it: {0:?}")]
204            RecordExists(PrettyPrintRecordKey<'static>),
205            // Missing some variants that exist in the current Error enum
206            #[error("Unknown error variant")]
207            #[serde(other)]
208            Unknown,
209        }
210
211        // Test serialization and deserialization of current Error
212        let current_error = Error::ScratchpadCipherTextInvalid;
213        let serialized_current = rmp_serde::to_vec(&current_error).unwrap();
214
215        // Test that we can deserialize into ReducedError (older version)
216        let deserialized_reduced: ReducedError =
217            rmp_serde::from_slice(&serialized_current).unwrap();
218        assert_eq!(
219            deserialized_reduced,
220            ReducedError::ScratchpadCipherTextInvalid
221        );
222
223        // Test that unknown variants fall back to Unknown
224        let unknown_variant = Error::ScratchpadCipherTextFailed;
225        let serialized_unknown = rmp_serde::to_vec(&unknown_variant).unwrap();
226        let deserialized_unknown: ReducedError =
227            rmp_serde::from_slice(&serialized_unknown).unwrap();
228        assert_eq!(deserialized_unknown, ReducedError::Unknown);
229
230        // Test reverse direction: ReducedError -> Error for simple variants
231        let reduced_simple_error = ReducedError::GetStoreQuoteFailed;
232        let serialized_reduced_simple = rmp_serde::to_vec(&reduced_simple_error).unwrap();
233        let deserialized_reduced_simple: Error =
234            rmp_serde::from_slice(&serialized_reduced_simple).unwrap();
235        assert_eq!(deserialized_reduced_simple, Error::GetStoreQuoteFailed);
236
237        // Test reverse direction: ReducedError -> Error for complex variants
238        let addr = NetworkAddress::from(PeerId::random());
239        let reduced_complex_error = ReducedError::ChunkDoesNotExist(addr.clone());
240        let serialized_reduced_complex = rmp_serde::to_vec(&reduced_complex_error).unwrap();
241        let deserialized_reduced_complex: Error =
242            rmp_serde::from_slice(&serialized_reduced_complex).unwrap();
243        assert_eq!(deserialized_reduced_complex, Error::ChunkDoesNotExist(addr));
244
245        // Test reverse direction: ReducedError -> Error for complex struct variants
246        let holder = NetworkAddress::from(PeerId::random());
247        let key = NetworkAddress::from(PeerId::random());
248        let reduced_struct_error = ReducedError::ReplicatedRecordNotFound {
249            holder: Box::new(holder.clone()),
250            key: Box::new(key.clone()),
251        };
252        let serialized_reduced_struct = rmp_serde::to_vec(&reduced_struct_error).unwrap();
253        let deserialized_reduced_struct: Error =
254            rmp_serde::from_slice(&serialized_reduced_struct).unwrap();
255        assert_eq!(
256            deserialized_reduced_struct,
257            Error::ReplicatedRecordNotFound {
258                holder: Box::new(holder),
259                key: Box::new(key),
260            }
261        );
262    }
263
264    #[test]
265    fn test_error_retro_compatibility_many_missing() {
266        // test with many missing variants
267        #[derive(Error, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
268        #[non_exhaustive]
269        enum ManyMissingVariants {
270            #[error("Chunk does not exist {0:?}")]
271            ChunkDoesNotExist(NetworkAddress),
272            #[error("There was an error getting the storecost from kademlia store")]
273            GetStoreQuoteFailed,
274            // Only keeping ChunkDoesNotExist and GetStoreQuoteFailed, removing all others
275            #[error("Unknown error variant")]
276            #[serde(other)]
277            Unknown,
278        }
279
280        // Test with ManyMissingVariants
281        let current_error = Error::ScratchpadCipherTextInvalid;
282        let serialized_current = rmp_serde::to_vec(&current_error).unwrap();
283        let deserialized_many_missing: ManyMissingVariants =
284            rmp_serde::from_slice(&serialized_current).unwrap();
285        assert_eq!(deserialized_many_missing, ManyMissingVariants::Unknown);
286
287        // Test that GetStoreQuoteFailed works with ManyMissingVariants
288        let chunk_error = Error::GetStoreQuoteFailed;
289        let serialized_chunk = rmp_serde::to_vec(&chunk_error).unwrap();
290        let deserialized_chunk: ManyMissingVariants =
291            rmp_serde::from_slice(&serialized_chunk).unwrap();
292        assert_eq!(deserialized_chunk, ManyMissingVariants::GetStoreQuoteFailed);
293
294        // Test the reverse direction: ManyMissingVariants::GetStoreQuoteFailed can be parsed as Error::GetStoreQuoteFailed
295        let many_missing_error = ManyMissingVariants::GetStoreQuoteFailed;
296        let serialized_many_missing = rmp_serde::to_vec(&many_missing_error).unwrap();
297        let deserialized_to_error: Error = rmp_serde::from_slice(&serialized_many_missing).unwrap();
298        assert_eq!(deserialized_to_error, Error::GetStoreQuoteFailed);
299
300        // Test bidirectional compatibility for complex variant ChunkDoesNotExist
301        let addr = NetworkAddress::from(PeerId::random());
302
303        // Test Error::ChunkDoesNotExist -> ManyMissingVariants::ChunkDoesNotExist
304        let chunk_error = Error::ChunkDoesNotExist(addr.clone());
305        let serialized_chunk = rmp_serde::to_vec(&chunk_error).unwrap();
306        let deserialized_chunk: ManyMissingVariants =
307            rmp_serde::from_slice(&serialized_chunk).unwrap();
308        assert_eq!(
309            deserialized_chunk,
310            ManyMissingVariants::ChunkDoesNotExist(addr.clone())
311        );
312
313        // Test ManyMissingVariants::ChunkDoesNotExist -> Error::ChunkDoesNotExist
314        let many_missing_chunk = ManyMissingVariants::ChunkDoesNotExist(addr.clone());
315        let serialized_many_missing_chunk = rmp_serde::to_vec(&many_missing_chunk).unwrap();
316        let deserialized_many_missing_chunk: Error =
317            rmp_serde::from_slice(&serialized_many_missing_chunk).unwrap();
318        assert_eq!(
319            deserialized_many_missing_chunk,
320            Error::ChunkDoesNotExist(addr)
321        );
322
323        // Test that other simple variants fall back to Unknown in ManyMissingVariants
324        let record_error = Error::RecordParsingFailed;
325        let serialized_record = rmp_serde::to_vec(&record_error).unwrap();
326        let deserialized_record: ManyMissingVariants =
327            rmp_serde::from_slice(&serialized_record).unwrap();
328        assert_eq!(deserialized_record, ManyMissingVariants::Unknown);
329    }
330
331    // ignore this test proves complex types retro compatibility is not supported yet
332    #[test]
333    fn test_error_retro_compatibility_complex_types() {
334        // test with complex types
335        #[derive(Error, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
336        #[non_exhaustive]
337        enum ComplexTypesRemoved {
338            #[error("Chunk does not exist {0:?}")]
339            ChunkDoesNotExist(NetworkAddress),
340            #[error("Failed to deserialize hex ScratchpadAddress")]
341            ScratchpadHexDeserializeFailed,
342            #[error("Failed to derive CipherText from encrypted_data")]
343            ScratchpadCipherTextFailed,
344            #[error("Provided cypher text is invalid")]
345            ScratchpadCipherTextInvalid,
346            #[error("There was an error getting the storecost from kademlia store")]
347            GetStoreQuoteFailed,
348            #[error("There was an error generating the payment quote")]
349            QuoteGenerationFailed,
350            //removed this variant
351            // - #[error("Peer {holder:?} cannot find Record {key:?}")]
352            // - ReplicatedRecordNotFound {
353            // -     holder: Box<NetworkAddress>,
354            // -     key: Box<NetworkAddress>,
355            // - },
356            #[error("Could not Serialize/Deserialize RecordHeader to/from Record")]
357            RecordHeaderParsingFailed,
358            #[error("Could not Serialize/Deserialize Record")]
359            RecordParsingFailed,
360            #[error("The record already exists, so do not charge for it: {0:?}")]
361            RecordExists(PrettyPrintRecordKey<'static>),
362            // Missing some variants that exist in the current Error enum
363            #[error("Unknown error variant")]
364            #[serde(other)]
365            Unknown,
366        }
367
368        // Test that complex types (ReplicatedRecordNotFound) fall back to Unknown
369        // when the variant is missing from the older version
370        let holder = NetworkAddress::from(PeerId::random());
371        let key = NetworkAddress::from(PeerId::random());
372        let complex_error = Error::ReplicatedRecordNotFound {
373            holder: Box::new(holder),
374            key: Box::new(key),
375        };
376        let serialized_complex = rmp_serde::to_vec(&complex_error).unwrap();
377
378        // // Below is what we would want to do, but it's not supported yet
379        // let deserialized_complex: ComplexTypesRemoved =
380        //     rmp_serde::from_slice(&serialized_complex).unwrap();
381        // assert_eq!(deserialized_complex, ComplexTypesRemoved::Unknown);
382        // // for now it's an error
383        assert!(rmp_serde::from_slice::<ComplexTypesRemoved>(&serialized_complex).is_err());
384
385        // Test that simple variants that exist in both work correctly
386        let simple_error = Error::ScratchpadCipherTextInvalid;
387        let serialized_simple = rmp_serde::to_vec(&simple_error).unwrap();
388        let deserialized_simple: ComplexTypesRemoved =
389            rmp_serde::from_slice(&serialized_simple).unwrap();
390        assert_eq!(
391            deserialized_simple,
392            ComplexTypesRemoved::ScratchpadCipherTextInvalid
393        );
394
395        // Test that other simple variants also work
396        let addr = NetworkAddress::from(PeerId::random());
397        let chunk_error = Error::ChunkDoesNotExist(addr.clone());
398        let serialized_chunk = rmp_serde::to_vec(&chunk_error).unwrap();
399        let deserialized_chunk: ComplexTypesRemoved =
400            rmp_serde::from_slice(&serialized_chunk).unwrap();
401        assert_eq!(
402            deserialized_chunk,
403            ComplexTypesRemoved::ChunkDoesNotExist(addr)
404        );
405    }
406}