#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u16)]
pub enum ErrorCode {
Ok = 0x0000,
OkPartial = 0x0001,
InvalidMagic = 0x0100,
InvalidVersion = 0x0101,
InvalidChecksum = 0x0102,
InvalidSignature = 0x0103,
TruncatedSegment = 0x0104,
InvalidManifest = 0x0105,
ManifestNotFound = 0x0106,
UnknownSegmentType = 0x0107,
AlignmentError = 0x0108,
DimensionMismatch = 0x0200,
EmptyIndex = 0x0201,
MetricUnsupported = 0x0202,
FilterParseError = 0x0203,
KTooLarge = 0x0204,
Timeout = 0x0205,
LockHeld = 0x0300,
LockStale = 0x0301,
DiskFull = 0x0302,
FsyncFailed = 0x0303,
SegmentTooLarge = 0x0304,
ReadOnly = 0x0305,
TileTrap = 0x0400,
TileOom = 0x0401,
TileTimeout = 0x0402,
TileInvalidMsg = 0x0403,
TileUnsupportedOp = 0x0404,
KeyNotFound = 0x0500,
KeyExpired = 0x0501,
DecryptFailed = 0x0502,
AlgoUnsupported = 0x0503,
AttestationInvalid = 0x0504,
PlatformUnsupported = 0x0505,
AttestationExpired = 0x0506,
KeyNotBound = 0x0507,
ParentNotFound = 0x0600,
ParentHashMismatch = 0x0601,
LineageBroken = 0x0602,
LineageCyclic = 0x0603,
UnsignedManifest = 0x0800,
ContentHashMismatch = 0x0801,
UnknownSigner = 0x0802,
EpochDriftExceeded = 0x0803,
Level1InvalidSignature = 0x0804,
QualityBelowThreshold = 0x0900,
BudgetTokensExhausted = 0x0901,
QueryBlacklisted = 0x0902,
CowMapCorrupt = 0x0700,
ClusterNotFound = 0x0701,
ParentChainBroken = 0x0702,
DeltaThresholdExceeded = 0x0703,
SnapshotFrozen = 0x0704,
MembershipInvalid = 0x0705,
GenerationStale = 0x0706,
KernelBindingMismatch = 0x0707,
DoubleRootCorrupt = 0x0708,
}
impl ErrorCode {
#[inline]
pub const fn category(self) -> u8 {
(self as u16 >> 8) as u8
}
#[inline]
pub const fn is_success(self) -> bool {
self.category() == 0x00
}
#[inline]
pub const fn is_format_error(self) -> bool {
self.category() == 0x01
}
#[inline]
pub const fn is_security_error(self) -> bool {
self.category() == 0x08
}
#[inline]
pub const fn is_quality_error(self) -> bool {
self.category() == 0x09
}
}
impl TryFrom<u16> for ErrorCode {
type Error = u16;
fn try_from(value: u16) -> Result<Self, Self::Error> {
match value {
0x0000 => Ok(Self::Ok),
0x0001 => Ok(Self::OkPartial),
0x0100 => Ok(Self::InvalidMagic),
0x0101 => Ok(Self::InvalidVersion),
0x0102 => Ok(Self::InvalidChecksum),
0x0103 => Ok(Self::InvalidSignature),
0x0104 => Ok(Self::TruncatedSegment),
0x0105 => Ok(Self::InvalidManifest),
0x0106 => Ok(Self::ManifestNotFound),
0x0107 => Ok(Self::UnknownSegmentType),
0x0108 => Ok(Self::AlignmentError),
0x0200 => Ok(Self::DimensionMismatch),
0x0201 => Ok(Self::EmptyIndex),
0x0202 => Ok(Self::MetricUnsupported),
0x0203 => Ok(Self::FilterParseError),
0x0204 => Ok(Self::KTooLarge),
0x0205 => Ok(Self::Timeout),
0x0300 => Ok(Self::LockHeld),
0x0301 => Ok(Self::LockStale),
0x0302 => Ok(Self::DiskFull),
0x0303 => Ok(Self::FsyncFailed),
0x0304 => Ok(Self::SegmentTooLarge),
0x0305 => Ok(Self::ReadOnly),
0x0400 => Ok(Self::TileTrap),
0x0401 => Ok(Self::TileOom),
0x0402 => Ok(Self::TileTimeout),
0x0403 => Ok(Self::TileInvalidMsg),
0x0404 => Ok(Self::TileUnsupportedOp),
0x0500 => Ok(Self::KeyNotFound),
0x0501 => Ok(Self::KeyExpired),
0x0502 => Ok(Self::DecryptFailed),
0x0503 => Ok(Self::AlgoUnsupported),
0x0504 => Ok(Self::AttestationInvalid),
0x0505 => Ok(Self::PlatformUnsupported),
0x0506 => Ok(Self::AttestationExpired),
0x0507 => Ok(Self::KeyNotBound),
0x0600 => Ok(Self::ParentNotFound),
0x0601 => Ok(Self::ParentHashMismatch),
0x0602 => Ok(Self::LineageBroken),
0x0603 => Ok(Self::LineageCyclic),
0x0800 => Ok(Self::UnsignedManifest),
0x0801 => Ok(Self::ContentHashMismatch),
0x0802 => Ok(Self::UnknownSigner),
0x0803 => Ok(Self::EpochDriftExceeded),
0x0804 => Ok(Self::Level1InvalidSignature),
0x0900 => Ok(Self::QualityBelowThreshold),
0x0901 => Ok(Self::BudgetTokensExhausted),
0x0902 => Ok(Self::QueryBlacklisted),
0x0700 => Ok(Self::CowMapCorrupt),
0x0701 => Ok(Self::ClusterNotFound),
0x0702 => Ok(Self::ParentChainBroken),
0x0703 => Ok(Self::DeltaThresholdExceeded),
0x0704 => Ok(Self::SnapshotFrozen),
0x0705 => Ok(Self::MembershipInvalid),
0x0706 => Ok(Self::GenerationStale),
0x0707 => Ok(Self::KernelBindingMismatch),
0x0708 => Ok(Self::DoubleRootCorrupt),
other => Err(other),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum RvfError {
Code(ErrorCode),
UnknownCode(u16),
BadMagic { expected: u32, got: u32 },
SizeMismatch { expected: usize, got: usize },
InvalidEnumValue { type_name: &'static str, value: u64 },
Security(crate::security::SecurityError),
QualityBelowThreshold {
quality: crate::quality::ResponseQuality,
reason: &'static str,
},
}
impl core::fmt::Display for RvfError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Code(c) => write!(f, "RVF error code 0x{:04X}", *c as u16),
Self::UnknownCode(v) => write!(f, "unknown RVF error code 0x{v:04X}"),
Self::BadMagic { expected, got } => {
write!(f, "bad magic: expected 0x{expected:08X}, got 0x{got:08X}")
}
Self::SizeMismatch { expected, got } => {
write!(f, "size mismatch: expected {expected}, got {got}")
}
Self::InvalidEnumValue { type_name, value } => {
write!(f, "invalid {type_name} value: {value}")
}
Self::Security(e) => write!(f, "security error: {e}"),
Self::QualityBelowThreshold { quality, reason } => {
write!(f, "quality below threshold ({quality:?}): {reason}")
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::format;
#[test]
fn error_code_round_trip_all() {
let codes: &[(u16, ErrorCode)] = &[
(0x0000, ErrorCode::Ok),
(0x0001, ErrorCode::OkPartial),
(0x0100, ErrorCode::InvalidMagic),
(0x0101, ErrorCode::InvalidVersion),
(0x0102, ErrorCode::InvalidChecksum),
(0x0103, ErrorCode::InvalidSignature),
(0x0104, ErrorCode::TruncatedSegment),
(0x0105, ErrorCode::InvalidManifest),
(0x0106, ErrorCode::ManifestNotFound),
(0x0107, ErrorCode::UnknownSegmentType),
(0x0108, ErrorCode::AlignmentError),
(0x0200, ErrorCode::DimensionMismatch),
(0x0201, ErrorCode::EmptyIndex),
(0x0202, ErrorCode::MetricUnsupported),
(0x0203, ErrorCode::FilterParseError),
(0x0204, ErrorCode::KTooLarge),
(0x0205, ErrorCode::Timeout),
(0x0300, ErrorCode::LockHeld),
(0x0301, ErrorCode::LockStale),
(0x0302, ErrorCode::DiskFull),
(0x0303, ErrorCode::FsyncFailed),
(0x0304, ErrorCode::SegmentTooLarge),
(0x0305, ErrorCode::ReadOnly),
(0x0400, ErrorCode::TileTrap),
(0x0401, ErrorCode::TileOom),
(0x0402, ErrorCode::TileTimeout),
(0x0403, ErrorCode::TileInvalidMsg),
(0x0404, ErrorCode::TileUnsupportedOp),
(0x0500, ErrorCode::KeyNotFound),
(0x0501, ErrorCode::KeyExpired),
(0x0502, ErrorCode::DecryptFailed),
(0x0503, ErrorCode::AlgoUnsupported),
(0x0504, ErrorCode::AttestationInvalid),
(0x0505, ErrorCode::PlatformUnsupported),
(0x0506, ErrorCode::AttestationExpired),
(0x0507, ErrorCode::KeyNotBound),
(0x0600, ErrorCode::ParentNotFound),
(0x0601, ErrorCode::ParentHashMismatch),
(0x0602, ErrorCode::LineageBroken),
(0x0603, ErrorCode::LineageCyclic),
(0x0800, ErrorCode::UnsignedManifest),
(0x0801, ErrorCode::ContentHashMismatch),
(0x0802, ErrorCode::UnknownSigner),
(0x0803, ErrorCode::EpochDriftExceeded),
(0x0804, ErrorCode::Level1InvalidSignature),
(0x0900, ErrorCode::QualityBelowThreshold),
(0x0901, ErrorCode::BudgetTokensExhausted),
(0x0902, ErrorCode::QueryBlacklisted),
(0x0700, ErrorCode::CowMapCorrupt),
(0x0701, ErrorCode::ClusterNotFound),
(0x0702, ErrorCode::ParentChainBroken),
(0x0703, ErrorCode::DeltaThresholdExceeded),
(0x0704, ErrorCode::SnapshotFrozen),
(0x0705, ErrorCode::MembershipInvalid),
(0x0706, ErrorCode::GenerationStale),
(0x0707, ErrorCode::KernelBindingMismatch),
(0x0708, ErrorCode::DoubleRootCorrupt),
];
for &(raw, expected) in codes {
assert_eq!(ErrorCode::try_from(raw), Ok(expected), "code 0x{raw:04X}");
assert_eq!(expected as u16, raw);
}
}
#[test]
fn unknown_code() {
assert_eq!(ErrorCode::try_from(0x9999), Err(0x9999));
}
#[test]
fn category_extraction() {
assert_eq!(ErrorCode::Ok.category(), 0x00);
assert_eq!(ErrorCode::InvalidMagic.category(), 0x01);
assert_eq!(ErrorCode::DimensionMismatch.category(), 0x02);
assert_eq!(ErrorCode::LockHeld.category(), 0x03);
assert_eq!(ErrorCode::TileTrap.category(), 0x04);
assert_eq!(ErrorCode::KeyNotFound.category(), 0x05);
assert_eq!(ErrorCode::ParentNotFound.category(), 0x06);
assert_eq!(ErrorCode::CowMapCorrupt.category(), 0x07);
assert_eq!(ErrorCode::UnsignedManifest.category(), 0x08);
assert_eq!(ErrorCode::QualityBelowThreshold.category(), 0x09);
}
#[test]
fn security_error_check() {
assert!(ErrorCode::UnsignedManifest.is_security_error());
assert!(!ErrorCode::Ok.is_security_error());
}
#[test]
fn quality_error_check() {
assert!(ErrorCode::QualityBelowThreshold.is_quality_error());
assert!(!ErrorCode::Ok.is_quality_error());
}
#[test]
fn success_check() {
assert!(ErrorCode::Ok.is_success());
assert!(ErrorCode::OkPartial.is_success());
assert!(!ErrorCode::InvalidMagic.is_success());
}
#[test]
fn format_error_check() {
assert!(ErrorCode::InvalidMagic.is_format_error());
assert!(!ErrorCode::Ok.is_format_error());
assert!(!ErrorCode::DimensionMismatch.is_format_error());
}
#[test]
fn rvf_error_display() {
let e = RvfError::BadMagic {
expected: 0x52564653,
got: 0x00000000,
};
let s = format!("{e}");
assert!(s.contains("bad magic"));
assert!(s.contains("52564653"));
}
#[test]
fn cow_error_check() {
assert_eq!(ErrorCode::CowMapCorrupt as u16, 0x0700);
assert_eq!(ErrorCode::ClusterNotFound as u16, 0x0701);
assert_eq!(ErrorCode::ParentChainBroken as u16, 0x0702);
assert_eq!(ErrorCode::DeltaThresholdExceeded as u16, 0x0703);
assert_eq!(ErrorCode::SnapshotFrozen as u16, 0x0704);
assert_eq!(ErrorCode::MembershipInvalid as u16, 0x0705);
assert_eq!(ErrorCode::GenerationStale as u16, 0x0706);
assert_eq!(ErrorCode::KernelBindingMismatch as u16, 0x0707);
assert_eq!(ErrorCode::DoubleRootCorrupt as u16, 0x0708);
assert_eq!(ErrorCode::CowMapCorrupt.category(), 0x07);
assert_eq!(ErrorCode::DoubleRootCorrupt.category(), 0x07);
}
#[test]
fn error_codes_match_spec() {
assert_eq!(ErrorCode::InvalidMagic as u16, 0x0100);
assert_eq!(ErrorCode::InvalidChecksum as u16, 0x0102);
assert_eq!(ErrorCode::ManifestNotFound as u16, 0x0106);
assert_eq!(ErrorCode::AlgoUnsupported as u16, 0x0503);
}
}