// Copyright 2023 MaidSafe.net limited.
//
// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3.
// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. Please review the Licences for the specific language governing
// permissions and limitations relating to use of the SAFE Network Software.
use crate::LinkError;
use sn_interface::{
messaging::{
data::{DataQueryVariant, Error as ErrorMsg, QueryResponse},
system::NodeMsg,
Error as MessagingError, MsgId, MsgType,
},
types::{Error as DtError, Peer},
};
use bls::PublicKey;
use sn_dbc::KeyImage;
use std::{io, time::Duration};
use thiserror::Error;
use xor_name::XorName;
/// Specialisation of `std::Result` for Client.
pub type Result<T, E = Error> = std::result::Result<T, E>;
/// Client Errors
#[allow(clippy::large_enum_variant)]
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum Error {
/// No section was found to be closest for given dataname/
/// This is indicative of some larger problem
#[error("No SAP for data name: {0}")]
NoCloseSapFound(XorName),
/// No elder was found to be closest from provided SAP. (The sap must therefore be empty)
#[error("No elders found in AntiEntropy msg SAP")]
AntiEntropyNoSapElders,
/// Maximum number of retries upon AntiEntropy responses was reached
#[error(
"Maximum number of retries upon AntiEntropy responses was reached. \
Message {msg_id:?} was re-sent {retries} times due to AE responses."
)]
AntiEntropyMaxRetries {
/// Id of the cmd message sent
msg_id: MsgId,
/// Number of times the msg was re-sent
retries: u8,
},
/// Failed to obtain network contacts to bootstrap to
#[error("Failed to obtain network contacts to bootstrap to: {0}")]
NetworkContacts(String),
/// InsufficientAcksReceived
#[error(
"Did not receive sufficient ACK messages from Elders to be sure this cmd ({msg_id:?}) \
passed, expected: {expected}, received {received}."
)]
InsufficientAcksReceived {
/// Id of the cmd message sent
msg_id: MsgId,
/// Number of expected ACKs
expected: usize,
/// Number of received ACKs
received: usize,
},
/// Initial network contact failed
#[error("Initial network contact probe failed. Attempted contacts: {0:?}")]
NetworkContact(Vec<Peer>),
/// Client has not gone through qp2p bootstrap process yet
#[error(
"Client has not yet acquired enough/any network knowledge for destination \
xorname {0}, so anything sent is guaranteed to have a lengthy AE process"
)]
NoNetworkKnowledge(XorName),
/// Could not connect to sufficient elder to retrieve reliable responses.
#[error(
"Problem connecting to sufficient elders. A supermajority of responses is unobtainable. \
{connections} were connected to, {required} needed."
)]
InsufficientElderConnections {
/// Number of existing connections to Elders
connections: usize,
/// Minimum number of connections to Elders required for the operation
required: usize,
},
/// Did not know of sufficient elders in the desired section to get supermajority of responses.
#[error(
"Problem finding sufficient elders. A supermajority of responses is unobtainable. \
{connections} were known in this section, {required} needed. Section pk: {section_pk:?}"
)]
InsufficientElderKnowledge {
/// Number of existing connections to Elders
connections: usize,
/// Minimum number of connections to Elders required for the operation
required: usize,
/// Public key of the target section
section_pk: PublicKey,
},
/// Cannot store empty file..
#[error("Cannot store empty file.")]
EmptyFileProvided,
/// Not enough bytes for self-encryption.
#[error(
"Not enough bytes ({size}) for self-encryption, at least {minimum} bytes needed. \
Try storing it as a SmallFile."
)]
TooSmallForSelfEncryption {
/// Number of bytes
size: usize,
/// Minimum number of bytes for self-encryption
minimum: usize,
},
#[cfg(feature = "limit-client-upload-size")]
/// Upload size exceeded current file size upload limit.
#[error(
"Too large file upload attempted ({size} bytes), at most {limit} bytes allowed currently. \
Try storing a smaller file."
)]
UploadSizeLimitExceeded {
/// Number of bytes attempted.
size: usize,
/// Size limit, number of bytes.
limit: usize,
},
/// Encryption oversized the SmallFile, so it cannot be stored as a SmallFile and be encrypted
#[error(
"You might need to pad the `SmallFile` contents and then store it as a `LargeFile`, \
as the encryption has made it slightly too big ({0} bytes)"
)]
SmallFilePaddingNeeded(usize),
/// The provided bytes is too large to store as a `SmallFile`.
#[error(
"The provided bytes ({size}) is too large to store as a `SmallFile` which maximum can be \
{maximum}. Store as a LargeFile instead."
)]
TooLargeAsSmallFile {
/// Number of bytes
size: usize,
/// Maximum number of bytes for a `SmallFile`
maximum: usize,
},
/// Timeout occurred when trying to verify chunk was uploaded
#[error("Timeout occurred after {elapsed:?} when trying to verify chunk at xorname address {address} was uploaded")]
ChunkUploadValidationTimeout {
/// Time elapsed before timing out
elapsed: Duration,
/// Address name of the chunk
address: XorName,
},
/// Remote peer closed the bi-stream we expected a response on
#[error("The bi-stream we expected a msg response on, for {msg_id:?}, was closed by remote peer: {peer:?}")]
ResponseStreamClosed {
/// MsgId of the msg sent
msg_id: MsgId,
/// Peer the msg was sent to
peer: Peer,
},
/// Failed to obtain a response from Elders.
#[error("Failed to obtain any response for {msg_id:?} from: {peers:?}")]
NoResponse {
/// MsgId of the msg sent
msg_id: MsgId,
/// Peers the msg was sent to
peers: Vec<Peer>,
},
/// Timeout when awaiting command ACK from Elders.
#[error("Timeout after {elapsed:?} when awaiting command ACK from Elders for data address {dst_address}")]
CmdAckValidationTimeout {
/// Time elapsed before timing out
elapsed: Duration,
/// Address name of the data the ACK was expected for
dst_address: XorName,
},
/// Unexpected query response received
#[error("Unexpected response received for {query:?}. Received: {response:?}")]
UnexpectedQueryResponse {
/// Query sent to Elders
query: DataQueryVariant,
/// Unexpected response received
response: QueryResponse,
},
/// Unexpected NodeMsg received
#[error("Unexpected type of NodeMsg received from {peer} in response to {correlation_id:?}. Received: {msg:?}")]
UnexpectedNodeMsg {
/// MsgId of the msg sent
correlation_id: MsgId,
/// Peer the unexpected msg was received from
peer: Peer,
/// Unexpected msg received
msg: NodeMsg,
},
/// Unexpected msg type received
#[error("Unexpected type of message received from {peer} in response to {correlation_id:?}. Received: {msg:?}")]
UnexpectedMsgType {
/// MsgId of the msg sent
correlation_id: MsgId,
/// Peer the unexpected msg was received from
peer: Peer,
/// Unexpected msg received
msg: MsgType,
},
/// Other types errors
#[error(transparent)]
NetworkDataError(#[from] DtError),
/// Errors received from the network via sn_messaging
#[error("Error received from the network: {source:?}")]
ErrorMsg {
/// The source of an error msg
source: ErrorMsg,
},
/// Error response received for a client cmd sent to the network
#[error("Error received from the network: {source:?} for cmd: {msg_id:?}")]
CmdError {
/// The source of an error msg
source: ErrorMsg,
/// MsgId of the cmd sent
msg_id: MsgId,
},
/// Errors occurred when serialising or deserialising msgs
#[error(transparent)]
MessagingProtocol(#[from] MessagingError),
/// Self-Enryption errors
#[error(transparent)]
SelfEncryption(#[from] self_encryption::Error),
/// Io error.
#[error(transparent)]
IoError(#[from] io::Error),
/// Endpoint setup error.
#[error(transparent)]
EndpointSetup(#[from] qp2p::ClientEndpointError),
/// QuicP2p Recv error.
#[error(transparent)]
QuicP2p(#[from] qp2p::RecvError),
/// QuicP2p Connection error.
#[error("Failed to stablish a connection with node {peer:?}: {error}.")]
QuicP2pConnection {
/// Node the connection was attempted to be stablished with
peer: Peer,
/// The error encountered when attempting to stablish the connection
error: qp2p::ConnectionError,
/// MsgId of the msg that was going to be sent
msg_id: MsgId,
},
/// QuicP2p Send error.
#[error("Failed to send a message to node {peer:?}: {error}.")]
QuicP2pSend {
/// Node the message was attempted to be sent to
peer: Peer,
/// The error encountered when attempting to send the message
error: qp2p::SendError,
/// MsgId of the msg attempted to send
msg_id: MsgId,
},
/// Bincode error
#[error(transparent)]
Serialisation(#[from] Box<bincode::ErrorKind>),
/// Could not retrieve all chunks required to decrypt the data. (expected, error)
#[error("Not all chunks were retrieved, expected {expected}, retrieved {retrieved}.")]
NotEnoughChunksRetrieved {
/// Number of Chunks expected to be retrieved
expected: usize,
/// Number of Chunks retrieved
retrieved: usize,
},
/// All attempts to initiate a bi-stream failed
#[error("Could no initiate bi-stream for {msg_id:?}: {error:?}")]
FailedToInitateBiDiStream {
/// Id of the message to be sent
msg_id: MsgId,
/// The error encountered when trying to initiate a bi-stream
error: LinkError,
},
/// Could not chunk all the data required to encrypt the data. (Expected, Actual)
#[error("Not all data was chunked, expected {expected}, but we have {chunked}.)")]
NotAllDataWasChunked {
/// Number of Chunks expected to be generated
expected: usize,
/// Number of Chunks generated
chunked: usize,
},
/// Occurs if a signed SAP cannot be obtained for a section key.
#[error("A signed section authority provider was not found for section key {0:?}")]
SignedSapNotFound(PublicKey),
/// Occurs if a DBC spend command eventually fails after a number of retry attempts.
#[error("The DBC spend request failed after {attempts} attempts for key_image: {key_image:?}")]
DbcSpendRetryAttemptsExceeded {
/// Number of attemtps made
attempts: u8,
/// The key_image that was attempted to spend
key_image: KeyImage,
},
/// Occurs if a section key is not found when searching the sections DAG.
#[error("Section key {0:?} was not found in the sections DAG")]
SectionsDagKeyNotFound(PublicKey),
/// Data replicas check errors
#[cfg(feature = "check-replicas")]
#[error(transparent)]
DataReplicasCheck(#[from] DataReplicasCheckError),
}
#[cfg(feature = "check-replicas")]
#[derive(Error, Debug)]
#[non_exhaustive]
/// Data replicas check errors
pub enum DataReplicasCheckError {
/// No response or error received when sending query to data replicas
#[error("No response or error obtained when sending query to all replicas: {0:?}")]
NoResponse(DataQueryVariant),
/// Errors received when checking data replicas
#[error("Errors occurred when sending the query to {}/{replicas} of the replicas: {query:?}. \
Errors received: {errors:?}", errors.len()
)]
ReceivedErrors {
/// Number of replicas queried
replicas: usize,
/// Query sent to data replicas
query: DataQueryVariant,
/// List of errors received with their corresponding replica/Adult index
errors: Vec<(Error, usize)>,
},
/// Not all responses received from data replicas are the same
#[error(
"Not all responses received are the same when sending query to {replicas} \
replicas: {query:?}. Responses received: {responses:?}"
)]
DifferentResponses {
/// Number of replicas queried
replicas: usize,
/// Query sent to data replicas
query: DataQueryVariant,
/// List of responses received with their corresponding replica/Adult index
responses: Vec<(crate::sessions::QueryResult, usize)>,
},
}