Skip to main content

kinetic_core/error/
identity.rs

1//! Node identity key errors (`KIN-IDN-NNN`).
2//!
3//! [`IdentityError`] is returned by [`load_keypair`](crate::types::load_keypair) and
4//! `save_keypair` when the ML-DSA-65 identity file is
5//! missing, truncated, or the BIP-39 seed phrase is malformed.
6//!
7//! The identity file at `{base_dir}/identity.key` stores the raw ML-DSA-65 signing
8//! key bytes and is required for daemon startup. If it is absent, a new key is generated.
9use super::Severity;
10use thiserror::Error;
11
12/// Error type for node identity keys and mnemonic parsing.
13#[derive(Error, Debug)]
14pub enum IdentityError {
15    /// An I/O error occurred while reading or writing the identity file.
16    #[error("I/O error: {0}")]
17    Io(#[from] std::io::Error),
18
19    /// The identity file is corrupted (e.g. wrong byte length).
20    #[error("Identity file is corrupted: {0}")]
21    CorruptedIdentityFile(String),
22
23    /// The identity file could not be found.
24    #[error("Identity not found: {0}")]
25    IdentityNotFound(String),
26
27    /// The provided BIP-39 mnemonic seed phrase is invalid.
28    #[error("Invalid seed phrase: {0}")]
29    InvalidSeedPhrase(String),
30    /// Failed to decrypt the identity file.
31    #[error("Failed to decrypt identity file: {0}")]
32    DecryptionFailed(String),
33}
34
35impl PartialEq for IdentityError {
36    fn eq(&self, other: &Self) -> bool {
37        match (self, other) {
38            (Self::Io(a), Self::Io(b)) => a.kind() == b.kind(),
39            (Self::CorruptedIdentityFile(a), Self::CorruptedIdentityFile(b)) => a == b,
40            (Self::IdentityNotFound(a), Self::IdentityNotFound(b)) => a == b,
41            (Self::InvalidSeedPhrase(a), Self::InvalidSeedPhrase(b)) => a == b,
42            (Self::DecryptionFailed(a), Self::DecryptionFailed(b)) => a == b,
43            _ => false,
44        }
45    }
46}
47
48impl Eq for IdentityError {}
49
50impl IdentityError {
51    /// Stable protocol error code.
52    pub fn code(&self) -> &'static str {
53        match self {
54            Self::Io(_) => "KIN-IDN-001",
55            Self::CorruptedIdentityFile(_) => "KIN-IDN-002",
56            Self::IdentityNotFound(_) => "KIN-IDN-003",
57            Self::InvalidSeedPhrase(_) => "KIN-IDN-004",
58            Self::DecryptionFailed(_) => "KIN-IDN-005",
59        }
60    }
61
62    /// RFC 7807 type URI for this error.
63    pub fn error_type_uri(&self) -> String {
64        format!("{}/errors/{}", crate::constants::DOCS_URL, self.code())
65    }
66
67    /// Severity level for logging and monitoring.
68    pub fn severity(&self) -> Severity {
69        match self {
70            Self::Io(_)
71            | Self::CorruptedIdentityFile(_)
72            | Self::IdentityNotFound(_)
73            | Self::DecryptionFailed(_) => Severity::Error,
74            Self::InvalidSeedPhrase(_) => Severity::Warning,
75        }
76    }
77
78    /// Whether the client should offer a retry action.
79    pub fn is_retryable(&self) -> bool {
80        false
81    }
82
83    /// Returns the user-facing message.
84    pub fn user_message(&self) -> String {
85        match self {
86            Self::Io(_) => {
87                "An I/O error occurred while reading or writing the identity file.".to_string()
88            }
89            Self::CorruptedIdentityFile(_) => {
90                "The identity file is corrupted and cannot be used.".to_string()
91            }
92            Self::IdentityNotFound(_) => "The identity file could not be found.".to_string(),
93            Self::InvalidSeedPhrase(_) => "The provided seed phrase is invalid.".to_string(),
94            Self::DecryptionFailed(_) => {
95                "Failed to decrypt the identity file. Incorrect password or corrupted payload."
96                    .to_string()
97            }
98        }
99    }
100}