Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion ant-cli/src/commands/data/chunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ impl PeerGetSummary {
self.not_found += 1;
"not_found".to_string()
}
Err(DataError::NotFound(e)) => {
self.not_found += 1;
format!("not_found message={e}")
}
Err(DataError::Timeout(e)) => {
self.timeout += 1;
format!("timeout message={e}")
Expand Down Expand Up @@ -359,6 +363,10 @@ mod tests {

assert_eq!(summary.record(&Ok(Some(chunk))), "found bytes=3");
assert_eq!(summary.record(&Ok(None)), "not_found");
assert_eq!(
summary.record(&Err(DataError::NotFound("nothing at abcd".to_string()))),
"not_found message=nothing at abcd"
);
assert_eq!(
summary.record(&Err(DataError::Timeout("slow".to_string()))),
"timeout message=slow"
Expand All @@ -373,7 +381,7 @@ mod tests {
);

assert_eq!(summary.found, 1);
assert_eq!(summary.not_found, 1);
assert_eq!(summary.not_found, 2);
assert_eq!(summary.timeout, 1);
assert_eq!(summary.network_error, 1);
assert_eq!(summary.error, 1);
Expand Down
21 changes: 12 additions & 9 deletions ant-core/src/data/client/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,10 +395,11 @@ impl Client {
///
/// # Errors
///
/// Returns an error if the chunk is not found or deserialization fails.
/// Returns [`Error::NotFound`] if no chunk exists at `address`; other
/// errors if retrieval or deserialization fails.
pub async fn data_map_fetch(&self, address: &[u8; 32]) -> Result<DataMap> {
let chunk = self.chunk_get(address).await?.ok_or_else(|| {
Error::InvalidData(format!(
Error::NotFound(format!(
"DataMap chunk not found at {}",
hex::encode(address)
))
Expand All @@ -412,7 +413,8 @@ impl Client {
///
/// # Errors
///
/// Returns an error if the chunk is not found or deserialization fails.
/// Returns [`Error::NotFound`] if no chunk exists at `address`; other
/// errors if retrieval or deserialization fails.
pub async fn data_map_fetch_from_closest_peers(
&self,
address: &[u8; 32],
Expand All @@ -422,7 +424,7 @@ impl Client {
.chunk_get_from_closest_peers(address, peer_count.get())
.await?
.ok_or_else(|| {
Error::InvalidData(format!(
Error::NotFound(format!(
"DataMap chunk not found at {}",
hex::encode(address)
))
Expand Down Expand Up @@ -451,8 +453,9 @@ impl Client {
///
/// # Errors
///
/// Returns an error if any chunk cannot be retrieved, if decryption fails,
/// or if a shrunk map must be resolved on a current-thread runtime.
/// Returns an error if any chunk cannot be retrieved (a chunk absent from
/// every queried peer surfaces as [`Error::NotFound`]), if decryption
/// fails, or if a shrunk map must be resolved on a current-thread runtime.
pub async fn data_download(&self, data_map: &DataMap) -> Result<Bytes> {
let root_data_map = self.resolve_root_data_map(data_map).await?;

Expand All @@ -479,7 +482,7 @@ impl Client {
// (Ok(None) -> Timeout is the load-shedding
// signal for sustained close-group exhaustion).
let chunk = self.chunk_get_observed(&address).await?.ok_or_else(|| {
Error::InvalidData(format!(
Error::NotFound(format!(
"Missing chunk {} required for data reconstruction",
hex::encode(address)
))
Expand Down Expand Up @@ -552,7 +555,7 @@ impl Client {

// The self-encryption fetcher may only yield `self_encryption::Error`.
// Capture the underlying `ant-core` error out-of-band so a missing
// wrapper chunk surfaces as `Error::InvalidData` (matching the
// wrapper chunk surfaces as `Error::NotFound` (matching the
// content-chunk path) and a network failure keeps its `Timeout` /
// `Network` classification, instead of every resolution failure
// flattening to `Error::Encryption`.
Expand All @@ -566,7 +569,7 @@ impl Client {
Ok(Some(chunk)) => Ok(chunk.content),
Ok(None) => Err(record_wrapper_fetch_error(
&mut fetch_error,
Error::InvalidData(format!(
Error::NotFound(format!(
"Missing wrapper chunk {} required to resolve root DataMap",
hex::encode(address)
)),
Expand Down
17 changes: 15 additions & 2 deletions ant-core/src/data/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,12 @@ pub(crate) const PUT_TARGET_WIDTH: usize = 20;
/// - `PartialUpload` -> `NetworkError` (literal capacity signal: some
/// chunks could not be stored)
/// - `AlreadyStored`, `Encryption`, `Crypto`, `Payment`,
/// `Serialization`, `InvalidData`, `SignatureVerification`,
/// `Serialization`, `InvalidData`, `NotFound`, `SignatureVerification`,
/// `Config`, `InsufficientDiskSpace`, `CostEstimationInconclusive`,
/// `Cancelled` -> `ApplicationError` (would happen on a perfectly
/// healthy link; `Cancelled` is caller-initiated and must not be retried
/// as a transport failure)
/// as a transport failure; `NotFound` is a definitively absent record
/// reported over a working link, not a transport symptom)
/// - `RemotePut` -> `ApplicationError` (the remote node responded with a
/// structured rejection — the transport succeeded, so the node declined
/// at the application layer; not a local capacity signal)
Expand All @@ -83,6 +84,10 @@ pub(crate) fn classify_error(err: &Error) -> Outcome {
| Error::Payment(_)
| Error::Serialization(_)
| Error::InvalidData(_)
// A definitively absent record, reported over a working link —
// the peers answered, there was just nothing stored there. Not a
// transport symptom, so it must not push the limiter down.
| Error::NotFound(_)
| Error::SignatureVerification(_)
| Error::Config(_)
| Error::InsufficientDiskSpace(_)
Expand Down Expand Up @@ -623,6 +628,13 @@ mod tests {
Error::InvalidData("d".to_string()),
Outcome::ApplicationError,
),
// A definitively absent record over a working link — the peers
// answered, nothing was stored there. Must NOT register as a
// capacity signal.
(
Error::NotFound("missing".to_string()),
Outcome::ApplicationError,
),
(
Error::Serialization("s".to_string()),
Outcome::ApplicationError,
Expand Down Expand Up @@ -759,6 +771,7 @@ mod tests {
| Error::Payment(_)
| Error::Protocol(_)
| Error::InvalidData(_)
| Error::NotFound(_)
| Error::Serialization(_)
| Error::Crypto(_)
| Error::Io(_)
Expand Down
20 changes: 20 additions & 0 deletions ant-core/src/data/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,17 @@ pub enum Error {
#[error("invalid data: {0}")]
InvalidData(String),

/// The requested record does not exist on the network.
///
/// A well-formed address with nothing stored at it — e.g. a `DataMap`
/// chunk lookup or a reconstruction fetch that came back empty from
/// every queried peer. Distinct from [`Error::InvalidData`], which means
/// content WAS retrieved but is malformed or fails integrity checks, so
/// callers can show "not found — check the address" instead of a
/// caller-bug message.
#[error("not found: {0}")]
NotFound(String),

/// Serialization error.
#[error("serialization error: {0}")]
Serialization(String),
Expand Down Expand Up @@ -230,6 +241,15 @@ mod tests {
assert_eq!(err.to_string(), "invalid data: bad hash");
}

#[test]
fn test_display_not_found() {
let err = Error::NotFound("DataMap chunk not found at abcd".to_string());
assert_eq!(
err.to_string(),
"not found: DataMap chunk not found at abcd"
);
}

#[test]
fn test_display_serialization() {
let err = Error::Serialization("decode failed".to_string());
Expand Down
Loading