dig_did/error.rs
1//! The `dig-did` error taxonomy (SPEC §6).
2//!
3//! Every fallible operation in this crate returns [`DidError`]. It wraps the underlying
4//! chia-wallet-sdk driver error (the byte-source-of-truth for puzzle construction, INV-4) and adds
5//! the DID-domain failure modes this crate raises directly — parse failures, fail-closed hydration
6//! guards, and the `did:chia:` address-codec errors.
7
8use chia_wallet_sdk::driver::DriverError;
9use thiserror::Error;
10
11/// The result type returned by every fallible `dig-did` operation.
12pub type DidResult<T> = Result<T, DidError>;
13
14/// Everything that can go wrong while building or parsing a DID spend.
15///
16/// The variants split into two families: errors *delegated* to the chia-wallet-sdk driver/signer
17/// (wrapped verbatim so the underlying cause is never lost), and DID-domain errors this crate
18/// raises itself (parse/hydration/codec guards, all fail-closed per SPEC §5).
19#[derive(Debug, Error)]
20pub enum DidError {
21 /// A chia-wallet-sdk driver operation failed (puzzle currying, spend construction, CLVM
22 /// evaluation). The wrapped [`DriverError`] carries the precise cause.
23 #[error("chia driver error: {0}")]
24 Driver(#[from] DriverError),
25
26 /// The signing calculator failed to derive the required signatures from the coin spends
27 /// (invalid puzzle/solution, an infinity public key in an `AGG_SIG` condition). The message is
28 /// the underlying signer error rendered as a string, so this crate does not leak the signer's
29 /// error type into its public surface.
30 #[error("signature calculation failed: {0}")]
31 Signer(String),
32
33 /// A coin/puzzle/solution could not be parsed as the expected shape.
34 #[error("failed to parse DID: {0}")]
35 Parse(String),
36
37 /// The supplied puzzle parsed successfully but is not a DID singleton.
38 #[error("coin is not a DID singleton")]
39 NotDid,
40
41 /// A `did:chia:1…` string was malformed or failed bech32m decoding.
42 #[error("invalid did:chia string: {0}")]
43 InvalidDidString(String),
44
45 /// A recovery operation supplied an inconsistent recovery configuration (list hash / required
46 /// verifications mismatch).
47 #[error("invalid recovery configuration: {0}")]
48 InvalidRecovery(String),
49
50 /// Hydration could not establish the lineage proof required to spend the DID (SPEC §5,
51 /// fail-closed).
52 #[error("missing lineage proof for DID")]
53 MissingLineage,
54
55 /// A parsed DID coin was missing the owner hint memo required to recreate its child (SPEC §5,
56 /// fail-closed).
57 #[error("missing owner hint on DID coin")]
58 MissingHint,
59
60 /// A chain-level precondition was violated (e.g. a supplied coin does not match the expected
61 /// launcher). The string states the specific violation.
62 #[error("chain precondition failed: {0}")]
63 Chain(String),
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn display_messages_are_descriptive() {
72 assert_eq!(DidError::NotDid.to_string(), "coin is not a DID singleton");
73 assert_eq!(
74 DidError::MissingLineage.to_string(),
75 "missing lineage proof for DID"
76 );
77 assert_eq!(
78 DidError::MissingHint.to_string(),
79 "missing owner hint on DID coin"
80 );
81 assert_eq!(
82 DidError::Parse("bad".into()).to_string(),
83 "failed to parse DID: bad"
84 );
85 assert_eq!(
86 DidError::InvalidDidString("nope".into()).to_string(),
87 "invalid did:chia string: nope"
88 );
89 assert_eq!(
90 DidError::InvalidRecovery("mismatch".into()).to_string(),
91 "invalid recovery configuration: mismatch"
92 );
93 assert_eq!(
94 DidError::Signer("boom".into()).to_string(),
95 "signature calculation failed: boom"
96 );
97 assert_eq!(
98 DidError::Chain("wrong launcher".into()).to_string(),
99 "chain precondition failed: wrong launcher"
100 );
101 }
102
103 #[test]
104 fn wraps_driver_errors_via_from() {
105 let driver = DriverError::InvalidSingletonStruct;
106 let err: DidError = driver.into();
107 assert!(matches!(err, DidError::Driver(_)));
108 assert!(err.to_string().starts_with("chia driver error:"));
109 }
110}