use crate::{NetworkAddress, PrettyPrintRecordKey};
use libp2p::kad::store;
use serde::{Deserialize, Serialize};
use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Error, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Error {
#[error("Chunk does not exist {0:?}")]
ChunkDoesNotExist(NetworkAddress),
#[error("Failed to derive CipherText from encrypted_data")]
ScratchpadCipherTextFailed,
#[error("Provided cypher text is invalid")]
ScratchpadCipherTextInvalid,
#[error("There was an error getting the storecost from kademlia store")]
GetStoreQuoteFailed,
#[error("There was an error generating the payment quote")]
QuoteGenerationFailed,
#[error("Peer {holder:?} cannot find Record {key:?}")]
ReplicatedRecordNotFound {
holder: Box<NetworkAddress>,
key: Box<NetworkAddress>,
},
#[error("Could not Serialize/Deserialize RecordHeader to/from Record")]
RecordHeaderParsingFailed,
#[error("Could not Serialize/Deserialize Record")]
RecordParsingFailed,
#[error("The record already exists, so do not charge for it: {0:?}")]
RecordExists(PrettyPrintRecordKey<'static>),
#[error("Error handling record put: {0}")]
PutRecordFailed(String),
#[error("Outdated record: with counter {counter}, expected any above {expected}")]
OutdatedRecordCounter { counter: u64, expected: u64 },
#[error("There was an error getting the Merkle candidate quote: {0}")]
GetMerkleCandidateQuoteFailed(String),
#[error("Failed to sign Merkle candidate node: {0}")]
FailedToSignMerkleCandidate(String),
#[error("Merkle payment verification failed: {0}")]
MerklePaymentVerificationFailed(String),
#[error(
"Topology verification failed: only {valid_count}/{total_paid} paid nodes in closest {closest_count}"
)]
TopologyVerificationFailed {
target_address: Box<NetworkAddress>,
valid_count: usize,
total_paid: usize,
closest_count: usize,
#[serde(with = "crate::peer_id_serde")]
node_peers: Vec<libp2p::PeerId>,
#[serde(with = "crate::peer_id_serde")]
paid_peers: Vec<libp2p::PeerId>,
},
#[error("Unknown error: the peer and you are using different protocol versions")]
#[serde(other)]
Unknown,
}
impl From<Error> for store::Error {
fn from(_err: Error) -> Self {
store::Error::ValueTooLarge
}
}
impl From<store::Error> for Error {
fn from(_err: store::Error) -> Self {
Error::RecordParsingFailed
}
}
#[cfg(test)]
mod tests {
use libp2p::PeerId;
use super::*;
#[test]
fn test_error_retro_compatibility() {
#[derive(Error, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
enum ExtendedError {
#[error("Chunk does not exist {0:?}")]
ChunkDoesNotExist(NetworkAddress),
#[error("Failed to deserialize hex ScratchpadAddress")]
ScratchpadHexDeserializeFailed,
#[error("Failed to derive CipherText from encrypted_data")]
ScratchpadCipherTextFailed,
#[error("Provided cypher text is invalid")]
ScratchpadCipherTextInvalid,
#[error("There was an error getting the storecost from kademlia store")]
GetStoreQuoteFailed,
#[error("There was an error generating the payment quote")]
QuoteGenerationFailed,
#[error("Peer {holder:?} cannot find Record {key:?}")]
ReplicatedRecordNotFound {
holder: Box<NetworkAddress>,
key: Box<NetworkAddress>,
},
#[error("Could not Serialize/Deserialize RecordHeader to/from Record")]
RecordHeaderParsingFailed,
#[error("Could not Serialize/Deserialize Record")]
RecordParsingFailed,
#[error("The record already exists, so do not charge for it: {0:?}")]
RecordExists(PrettyPrintRecordKey<'static>),
#[error("New error variant for testing")]
NewErrorVariant,
#[error("Unknown error variant")]
#[serde(other)]
Unknown,
}
let extended_error = ExtendedError::NewErrorVariant;
let serialized = rmp_serde::to_vec(&extended_error).unwrap();
let deserialized: Error = rmp_serde::from_slice(&serialized).unwrap();
assert_eq!(deserialized, Error::Unknown);
}
#[test]
fn test_error_retro_compatibility_reduced() {
#[derive(Error, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
enum ReducedError {
#[error("Chunk does not exist {0:?}")]
ChunkDoesNotExist(NetworkAddress),
#[error("Failed to deserialize hex ScratchpadAddress")]
ScratchpadHexDeserializeFailed,
#[error("Provided cypher text is invalid")]
ScratchpadCipherTextInvalid,
#[error("There was an error getting the storecost from kademlia store")]
GetStoreQuoteFailed,
#[error("There was an error generating the payment quote")]
QuoteGenerationFailed,
#[error("Peer {holder:?} cannot find Record {key:?}")]
ReplicatedRecordNotFound {
holder: Box<NetworkAddress>,
key: Box<NetworkAddress>,
},
#[error("Could not Serialize/Deserialize RecordHeader to/from Record")]
RecordHeaderParsingFailed,
#[error("Could not Serialize/Deserialize Record")]
RecordParsingFailed,
#[error("The record already exists, so do not charge for it: {0:?}")]
RecordExists(PrettyPrintRecordKey<'static>),
#[error("Unknown error variant")]
#[serde(other)]
Unknown,
}
let current_error = Error::ScratchpadCipherTextInvalid;
let serialized_current = rmp_serde::to_vec(¤t_error).unwrap();
let deserialized_reduced: ReducedError =
rmp_serde::from_slice(&serialized_current).unwrap();
assert_eq!(
deserialized_reduced,
ReducedError::ScratchpadCipherTextInvalid
);
let unknown_variant = Error::ScratchpadCipherTextFailed;
let serialized_unknown = rmp_serde::to_vec(&unknown_variant).unwrap();
let deserialized_unknown: ReducedError =
rmp_serde::from_slice(&serialized_unknown).unwrap();
assert_eq!(deserialized_unknown, ReducedError::Unknown);
let reduced_simple_error = ReducedError::GetStoreQuoteFailed;
let serialized_reduced_simple = rmp_serde::to_vec(&reduced_simple_error).unwrap();
let deserialized_reduced_simple: Error =
rmp_serde::from_slice(&serialized_reduced_simple).unwrap();
assert_eq!(deserialized_reduced_simple, Error::GetStoreQuoteFailed);
let addr = NetworkAddress::from(PeerId::random());
let reduced_complex_error = ReducedError::ChunkDoesNotExist(addr.clone());
let serialized_reduced_complex = rmp_serde::to_vec(&reduced_complex_error).unwrap();
let deserialized_reduced_complex: Error =
rmp_serde::from_slice(&serialized_reduced_complex).unwrap();
assert_eq!(deserialized_reduced_complex, Error::ChunkDoesNotExist(addr));
let holder = NetworkAddress::from(PeerId::random());
let key = NetworkAddress::from(PeerId::random());
let reduced_struct_error = ReducedError::ReplicatedRecordNotFound {
holder: Box::new(holder.clone()),
key: Box::new(key.clone()),
};
let serialized_reduced_struct = rmp_serde::to_vec(&reduced_struct_error).unwrap();
let deserialized_reduced_struct: Error =
rmp_serde::from_slice(&serialized_reduced_struct).unwrap();
assert_eq!(
deserialized_reduced_struct,
Error::ReplicatedRecordNotFound {
holder: Box::new(holder),
key: Box::new(key),
}
);
}
#[test]
fn test_error_retro_compatibility_many_missing() {
#[derive(Error, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
enum ManyMissingVariants {
#[error("Chunk does not exist {0:?}")]
ChunkDoesNotExist(NetworkAddress),
#[error("There was an error getting the storecost from kademlia store")]
GetStoreQuoteFailed,
#[error("Unknown error variant")]
#[serde(other)]
Unknown,
}
let current_error = Error::ScratchpadCipherTextInvalid;
let serialized_current = rmp_serde::to_vec(¤t_error).unwrap();
let deserialized_many_missing: ManyMissingVariants =
rmp_serde::from_slice(&serialized_current).unwrap();
assert_eq!(deserialized_many_missing, ManyMissingVariants::Unknown);
let chunk_error = Error::GetStoreQuoteFailed;
let serialized_chunk = rmp_serde::to_vec(&chunk_error).unwrap();
let deserialized_chunk: ManyMissingVariants =
rmp_serde::from_slice(&serialized_chunk).unwrap();
assert_eq!(deserialized_chunk, ManyMissingVariants::GetStoreQuoteFailed);
let many_missing_error = ManyMissingVariants::GetStoreQuoteFailed;
let serialized_many_missing = rmp_serde::to_vec(&many_missing_error).unwrap();
let deserialized_to_error: Error = rmp_serde::from_slice(&serialized_many_missing).unwrap();
assert_eq!(deserialized_to_error, Error::GetStoreQuoteFailed);
let addr = NetworkAddress::from(PeerId::random());
let chunk_error = Error::ChunkDoesNotExist(addr.clone());
let serialized_chunk = rmp_serde::to_vec(&chunk_error).unwrap();
let deserialized_chunk: ManyMissingVariants =
rmp_serde::from_slice(&serialized_chunk).unwrap();
assert_eq!(
deserialized_chunk,
ManyMissingVariants::ChunkDoesNotExist(addr.clone())
);
let many_missing_chunk = ManyMissingVariants::ChunkDoesNotExist(addr.clone());
let serialized_many_missing_chunk = rmp_serde::to_vec(&many_missing_chunk).unwrap();
let deserialized_many_missing_chunk: Error =
rmp_serde::from_slice(&serialized_many_missing_chunk).unwrap();
assert_eq!(
deserialized_many_missing_chunk,
Error::ChunkDoesNotExist(addr)
);
let record_error = Error::RecordParsingFailed;
let serialized_record = rmp_serde::to_vec(&record_error).unwrap();
let deserialized_record: ManyMissingVariants =
rmp_serde::from_slice(&serialized_record).unwrap();
assert_eq!(deserialized_record, ManyMissingVariants::Unknown);
}
#[test]
fn test_error_retro_compatibility_complex_types() {
#[derive(Error, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
enum ComplexTypesRemoved {
#[error("Chunk does not exist {0:?}")]
ChunkDoesNotExist(NetworkAddress),
#[error("Failed to deserialize hex ScratchpadAddress")]
ScratchpadHexDeserializeFailed,
#[error("Failed to derive CipherText from encrypted_data")]
ScratchpadCipherTextFailed,
#[error("Provided cypher text is invalid")]
ScratchpadCipherTextInvalid,
#[error("There was an error getting the storecost from kademlia store")]
GetStoreQuoteFailed,
#[error("There was an error generating the payment quote")]
QuoteGenerationFailed,
#[error("Could not Serialize/Deserialize RecordHeader to/from Record")]
RecordHeaderParsingFailed,
#[error("Could not Serialize/Deserialize Record")]
RecordParsingFailed,
#[error("The record already exists, so do not charge for it: {0:?}")]
RecordExists(PrettyPrintRecordKey<'static>),
#[error("Unknown error variant")]
#[serde(other)]
Unknown,
}
let holder = NetworkAddress::from(PeerId::random());
let key = NetworkAddress::from(PeerId::random());
let complex_error = Error::ReplicatedRecordNotFound {
holder: Box::new(holder),
key: Box::new(key),
};
let serialized_complex = rmp_serde::to_vec(&complex_error).unwrap();
assert!(rmp_serde::from_slice::<ComplexTypesRemoved>(&serialized_complex).is_err());
let simple_error = Error::ScratchpadCipherTextInvalid;
let serialized_simple = rmp_serde::to_vec(&simple_error).unwrap();
let deserialized_simple: ComplexTypesRemoved =
rmp_serde::from_slice(&serialized_simple).unwrap();
assert_eq!(
deserialized_simple,
ComplexTypesRemoved::ScratchpadCipherTextInvalid
);
let addr = NetworkAddress::from(PeerId::random());
let chunk_error = Error::ChunkDoesNotExist(addr.clone());
let serialized_chunk = rmp_serde::to_vec(&chunk_error).unwrap();
let deserialized_chunk: ComplexTypesRemoved =
rmp_serde::from_slice(&serialized_chunk).unwrap();
assert_eq!(
deserialized_chunk,
ComplexTypesRemoved::ChunkDoesNotExist(addr)
);
}
}