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    // Dev Note: add new variants above this one for backward compatibility with older protocol versions
77    // ---------- Unknown/fallback variant for retro compatibility
78    /// Unknown error variant (for backward compatibility with newer protocol versions)
79    #[error("Unknown error: the peer and you are using different protocol versions")]
80    #[serde(other)]
81    Unknown,
82}
83
84impl From<Error> for store::Error {
85    fn from(_err: Error) -> Self {
86        store::Error::ValueTooLarge
87    }
88}
89
90impl From<store::Error> for Error {
91    fn from(_err: store::Error) -> Self {
92        Error::RecordParsingFailed
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use libp2p::PeerId;
99
100    use super::*;
101
102    #[test]
103    fn test_error_retro_compatibility() {
104        // Test with a new struct that has a new variant
105        #[derive(Error, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
106        #[non_exhaustive]
107        enum ExtendedError {
108            #[error("Chunk does not exist {0:?}")]
109            ChunkDoesNotExist(NetworkAddress),
110            #[error("Failed to deserialize hex ScratchpadAddress")]
111            ScratchpadHexDeserializeFailed,
112            #[error("Failed to derive CipherText from encrypted_data")]
113            ScratchpadCipherTextFailed,
114            #[error("Provided cypher text is invalid")]
115            ScratchpadCipherTextInvalid,
116            #[error("There was an error getting the storecost from kademlia store")]
117            GetStoreQuoteFailed,
118            #[error("There was an error generating the payment quote")]
119            QuoteGenerationFailed,
120            #[error("Peer {holder:?} cannot find Record {key:?}")]
121            ReplicatedRecordNotFound {
122                holder: Box<NetworkAddress>,
123                key: Box<NetworkAddress>,
124            },
125            #[error("Could not Serialize/Deserialize RecordHeader to/from Record")]
126            RecordHeaderParsingFailed,
127            #[error("Could not Serialize/Deserialize Record")]
128            RecordParsingFailed,
129            #[error("The record already exists, so do not charge for it: {0:?}")]
130            RecordExists(PrettyPrintRecordKey<'static>),
131            // New variant that doesn't exist in the original Error enum
132            #[error("New error variant for testing")]
133            NewErrorVariant,
134            #[error("Unknown error variant")]
135            #[serde(other)]
136            Unknown,
137        }
138
139        // Test serialization and deserialization of ExtendedError
140        let extended_error = ExtendedError::NewErrorVariant;
141        let serialized = rmp_serde::to_vec(&extended_error).unwrap();
142
143        // Test that we can deserialize into the current Error enum
144        let deserialized: Error = rmp_serde::from_slice(&serialized).unwrap();
145        assert_eq!(deserialized, Error::Unknown);
146    }
147
148    #[test]
149    fn test_error_retro_compatibility_reduced() {
150        // Test with a struct that has a missing variant (simulating older version)
151        #[derive(Error, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
152        #[non_exhaustive]
153        enum ReducedError {
154            #[error("Chunk does not exist {0:?}")]
155            ChunkDoesNotExist(NetworkAddress),
156            #[error("Failed to deserialize hex ScratchpadAddress")]
157            ScratchpadHexDeserializeFailed,
158            // removed this variant
159            // - #[error("Failed to derive CipherText from encrypted_data")]
160            // - ScratchpadCipherTextFailed,
161            #[error("Provided cypher text is invalid")]
162            ScratchpadCipherTextInvalid,
163            #[error("There was an error getting the storecost from kademlia store")]
164            GetStoreQuoteFailed,
165            #[error("There was an error generating the payment quote")]
166            QuoteGenerationFailed,
167            #[error("Peer {holder:?} cannot find Record {key:?}")]
168            ReplicatedRecordNotFound {
169                holder: Box<NetworkAddress>,
170                key: Box<NetworkAddress>,
171            },
172            #[error("Could not Serialize/Deserialize RecordHeader to/from Record")]
173            RecordHeaderParsingFailed,
174            #[error("Could not Serialize/Deserialize Record")]
175            RecordParsingFailed,
176            #[error("The record already exists, so do not charge for it: {0:?}")]
177            RecordExists(PrettyPrintRecordKey<'static>),
178            // Missing some variants that exist in the current Error enum
179            #[error("Unknown error variant")]
180            #[serde(other)]
181            Unknown,
182        }
183
184        // Test serialization and deserialization of current Error
185        let current_error = Error::ScratchpadCipherTextInvalid;
186        let serialized_current = rmp_serde::to_vec(&current_error).unwrap();
187
188        // Test that we can deserialize into ReducedError (older version)
189        let deserialized_reduced: ReducedError =
190            rmp_serde::from_slice(&serialized_current).unwrap();
191        assert_eq!(
192            deserialized_reduced,
193            ReducedError::ScratchpadCipherTextInvalid
194        );
195
196        // Test that unknown variants fall back to Unknown
197        let unknown_variant = Error::ScratchpadCipherTextFailed;
198        let serialized_unknown = rmp_serde::to_vec(&unknown_variant).unwrap();
199        let deserialized_unknown: ReducedError =
200            rmp_serde::from_slice(&serialized_unknown).unwrap();
201        assert_eq!(deserialized_unknown, ReducedError::Unknown);
202
203        // Test reverse direction: ReducedError -> Error for simple variants
204        let reduced_simple_error = ReducedError::GetStoreQuoteFailed;
205        let serialized_reduced_simple = rmp_serde::to_vec(&reduced_simple_error).unwrap();
206        let deserialized_reduced_simple: Error =
207            rmp_serde::from_slice(&serialized_reduced_simple).unwrap();
208        assert_eq!(deserialized_reduced_simple, Error::GetStoreQuoteFailed);
209
210        // Test reverse direction: ReducedError -> Error for complex variants
211        let addr = NetworkAddress::from(PeerId::random());
212        let reduced_complex_error = ReducedError::ChunkDoesNotExist(addr.clone());
213        let serialized_reduced_complex = rmp_serde::to_vec(&reduced_complex_error).unwrap();
214        let deserialized_reduced_complex: Error =
215            rmp_serde::from_slice(&serialized_reduced_complex).unwrap();
216        assert_eq!(deserialized_reduced_complex, Error::ChunkDoesNotExist(addr));
217
218        // Test reverse direction: ReducedError -> Error for complex struct variants
219        let holder = NetworkAddress::from(PeerId::random());
220        let key = NetworkAddress::from(PeerId::random());
221        let reduced_struct_error = ReducedError::ReplicatedRecordNotFound {
222            holder: Box::new(holder.clone()),
223            key: Box::new(key.clone()),
224        };
225        let serialized_reduced_struct = rmp_serde::to_vec(&reduced_struct_error).unwrap();
226        let deserialized_reduced_struct: Error =
227            rmp_serde::from_slice(&serialized_reduced_struct).unwrap();
228        assert_eq!(
229            deserialized_reduced_struct,
230            Error::ReplicatedRecordNotFound {
231                holder: Box::new(holder),
232                key: Box::new(key),
233            }
234        );
235    }
236
237    #[test]
238    fn test_error_retro_compatibility_many_missing() {
239        // test with many missing variants
240        #[derive(Error, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
241        #[non_exhaustive]
242        enum ManyMissingVariants {
243            #[error("Chunk does not exist {0:?}")]
244            ChunkDoesNotExist(NetworkAddress),
245            #[error("There was an error getting the storecost from kademlia store")]
246            GetStoreQuoteFailed,
247            // Only keeping ChunkDoesNotExist and GetStoreQuoteFailed, removing all others
248            #[error("Unknown error variant")]
249            #[serde(other)]
250            Unknown,
251        }
252
253        // Test with ManyMissingVariants
254        let current_error = Error::ScratchpadCipherTextInvalid;
255        let serialized_current = rmp_serde::to_vec(&current_error).unwrap();
256        let deserialized_many_missing: ManyMissingVariants =
257            rmp_serde::from_slice(&serialized_current).unwrap();
258        assert_eq!(deserialized_many_missing, ManyMissingVariants::Unknown);
259
260        // Test that GetStoreQuoteFailed works with ManyMissingVariants
261        let chunk_error = Error::GetStoreQuoteFailed;
262        let serialized_chunk = rmp_serde::to_vec(&chunk_error).unwrap();
263        let deserialized_chunk: ManyMissingVariants =
264            rmp_serde::from_slice(&serialized_chunk).unwrap();
265        assert_eq!(deserialized_chunk, ManyMissingVariants::GetStoreQuoteFailed);
266
267        // Test the reverse direction: ManyMissingVariants::GetStoreQuoteFailed can be parsed as Error::GetStoreQuoteFailed
268        let many_missing_error = ManyMissingVariants::GetStoreQuoteFailed;
269        let serialized_many_missing = rmp_serde::to_vec(&many_missing_error).unwrap();
270        let deserialized_to_error: Error = rmp_serde::from_slice(&serialized_many_missing).unwrap();
271        assert_eq!(deserialized_to_error, Error::GetStoreQuoteFailed);
272
273        // Test bidirectional compatibility for complex variant ChunkDoesNotExist
274        let addr = NetworkAddress::from(PeerId::random());
275
276        // Test Error::ChunkDoesNotExist -> ManyMissingVariants::ChunkDoesNotExist
277        let chunk_error = Error::ChunkDoesNotExist(addr.clone());
278        let serialized_chunk = rmp_serde::to_vec(&chunk_error).unwrap();
279        let deserialized_chunk: ManyMissingVariants =
280            rmp_serde::from_slice(&serialized_chunk).unwrap();
281        assert_eq!(
282            deserialized_chunk,
283            ManyMissingVariants::ChunkDoesNotExist(addr.clone())
284        );
285
286        // Test ManyMissingVariants::ChunkDoesNotExist -> Error::ChunkDoesNotExist
287        let many_missing_chunk = ManyMissingVariants::ChunkDoesNotExist(addr.clone());
288        let serialized_many_missing_chunk = rmp_serde::to_vec(&many_missing_chunk).unwrap();
289        let deserialized_many_missing_chunk: Error =
290            rmp_serde::from_slice(&serialized_many_missing_chunk).unwrap();
291        assert_eq!(
292            deserialized_many_missing_chunk,
293            Error::ChunkDoesNotExist(addr)
294        );
295
296        // Test that other simple variants fall back to Unknown in ManyMissingVariants
297        let record_error = Error::RecordParsingFailed;
298        let serialized_record = rmp_serde::to_vec(&record_error).unwrap();
299        let deserialized_record: ManyMissingVariants =
300            rmp_serde::from_slice(&serialized_record).unwrap();
301        assert_eq!(deserialized_record, ManyMissingVariants::Unknown);
302    }
303
304    // ignore this test proves complex types retro compatibility is not supported yet
305    #[test]
306    fn test_error_retro_compatibility_complex_types() {
307        // test with complex types
308        #[derive(Error, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
309        #[non_exhaustive]
310        enum ComplexTypesRemoved {
311            #[error("Chunk does not exist {0:?}")]
312            ChunkDoesNotExist(NetworkAddress),
313            #[error("Failed to deserialize hex ScratchpadAddress")]
314            ScratchpadHexDeserializeFailed,
315            #[error("Failed to derive CipherText from encrypted_data")]
316            ScratchpadCipherTextFailed,
317            #[error("Provided cypher text is invalid")]
318            ScratchpadCipherTextInvalid,
319            #[error("There was an error getting the storecost from kademlia store")]
320            GetStoreQuoteFailed,
321            #[error("There was an error generating the payment quote")]
322            QuoteGenerationFailed,
323            //removed this variant
324            // - #[error("Peer {holder:?} cannot find Record {key:?}")]
325            // - ReplicatedRecordNotFound {
326            // -     holder: Box<NetworkAddress>,
327            // -     key: Box<NetworkAddress>,
328            // - },
329            #[error("Could not Serialize/Deserialize RecordHeader to/from Record")]
330            RecordHeaderParsingFailed,
331            #[error("Could not Serialize/Deserialize Record")]
332            RecordParsingFailed,
333            #[error("The record already exists, so do not charge for it: {0:?}")]
334            RecordExists(PrettyPrintRecordKey<'static>),
335            // Missing some variants that exist in the current Error enum
336            #[error("Unknown error variant")]
337            #[serde(other)]
338            Unknown,
339        }
340
341        // Test that complex types (ReplicatedRecordNotFound) fall back to Unknown
342        // when the variant is missing from the older version
343        let holder = NetworkAddress::from(PeerId::random());
344        let key = NetworkAddress::from(PeerId::random());
345        let complex_error = Error::ReplicatedRecordNotFound {
346            holder: Box::new(holder),
347            key: Box::new(key),
348        };
349        let serialized_complex = rmp_serde::to_vec(&complex_error).unwrap();
350
351        // // Below is what we would want to do, but it's not supported yet
352        // let deserialized_complex: ComplexTypesRemoved =
353        //     rmp_serde::from_slice(&serialized_complex).unwrap();
354        // assert_eq!(deserialized_complex, ComplexTypesRemoved::Unknown);
355        // // for now it's an error
356        assert!(rmp_serde::from_slice::<ComplexTypesRemoved>(&serialized_complex).is_err());
357
358        // Test that simple variants that exist in both work correctly
359        let simple_error = Error::ScratchpadCipherTextInvalid;
360        let serialized_simple = rmp_serde::to_vec(&simple_error).unwrap();
361        let deserialized_simple: ComplexTypesRemoved =
362            rmp_serde::from_slice(&serialized_simple).unwrap();
363        assert_eq!(
364            deserialized_simple,
365            ComplexTypesRemoved::ScratchpadCipherTextInvalid
366        );
367
368        // Test that other simple variants also work
369        let addr = NetworkAddress::from(PeerId::random());
370        let chunk_error = Error::ChunkDoesNotExist(addr.clone());
371        let serialized_chunk = rmp_serde::to_vec(&chunk_error).unwrap();
372        let deserialized_chunk: ComplexTypesRemoved =
373            rmp_serde::from_slice(&serialized_chunk).unwrap();
374        assert_eq!(
375            deserialized_chunk,
376            ComplexTypesRemoved::ChunkDoesNotExist(addr)
377        );
378    }
379}