Skip to main content

exo_authority/
error.rs

1// Copyright 2026 Exochain Foundation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at:
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15// SPDX-License-Identifier: Apache-2.0
16
17//! Authority-specific error types.
18
19use thiserror::Error;
20
21/// Errors that can occur during authority chain validation and delegation operations.
22#[derive(Debug, Error, PartialEq, Eq, Clone)]
23pub enum AuthorityError {
24    #[error("chain broken at index {index}: {reason}")]
25    ChainBroken { index: usize, reason: String },
26
27    #[error("delegation depth {depth} exceeds maximum {max_depth}")]
28    DepthExceeded { depth: usize, max_depth: usize },
29
30    #[error("scope widening detected at index {index}")]
31    ScopeWidening { index: usize },
32
33    #[error("expired link at index {index}")]
34    ExpiredLink { index: usize },
35
36    #[error("invalid signature at index {index}")]
37    InvalidSignature { index: usize },
38
39    #[error("delegation signing payload encoding failed: {reason}")]
40    SigningPayloadEncoding { reason: String },
41
42    #[error("delegation audit hash encoding failed: {reason}")]
43    AuditHashEncoding { reason: String },
44
45    #[error("delegation audit chain broken at sequence {sequence}")]
46    AuditChainBroken { sequence: u64 },
47
48    #[error("invalid delegation: {reason}")]
49    InvalidDelegation { reason: String },
50
51    #[error("duplicate delegation: {id}")]
52    DuplicateDelegation { id: String },
53
54    #[error("circular delegation detected: {0}")]
55    CircularDelegation(String),
56
57    #[error("delegation not found: {0}")]
58    NotFound(String),
59
60    #[error("empty chain")]
61    EmptyChain,
62
63    #[error("permission not granted: {0}")]
64    PermissionDenied(String),
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn error_display_chain_broken() {
73        let e = AuthorityError::ChainBroken {
74            index: 2,
75            reason: "gap".into(),
76        };
77        assert!(e.to_string().contains("2"));
78        assert!(e.to_string().contains("gap"));
79    }
80
81    #[test]
82    fn error_display_depth_exceeded() {
83        let e = AuthorityError::DepthExceeded {
84            depth: 6,
85            max_depth: 5,
86        };
87        assert!(e.to_string().contains("6"));
88    }
89
90    #[test]
91    fn error_display_scope_widening() {
92        let e = AuthorityError::ScopeWidening { index: 1 };
93        assert!(e.to_string().contains("1"));
94    }
95
96    #[test]
97    fn error_display_expired_link() {
98        let e = AuthorityError::ExpiredLink { index: 0 };
99        assert!(e.to_string().contains("0"));
100    }
101
102    #[test]
103    fn error_display_invalid_signature() {
104        let e = AuthorityError::InvalidSignature { index: 3 };
105        assert!(e.to_string().contains("3"));
106    }
107
108    #[test]
109    fn error_display_signing_payload_encoding() {
110        let e = AuthorityError::SigningPayloadEncoding {
111            reason: "writer".into(),
112        };
113        assert!(e.to_string().contains("writer"));
114    }
115
116    #[test]
117    fn error_display_audit_hash_encoding() {
118        let e = AuthorityError::AuditHashEncoding {
119            reason: "writer".into(),
120        };
121        assert!(e.to_string().contains("writer"));
122    }
123
124    #[test]
125    fn error_display_audit_chain_broken() {
126        let e = AuthorityError::AuditChainBroken { sequence: 7 };
127        assert!(e.to_string().contains("7"));
128    }
129
130    #[test]
131    fn error_display_invalid_delegation() {
132        let e = AuthorityError::InvalidDelegation {
133            reason: "empty scope".into(),
134        };
135        assert!(e.to_string().contains("empty scope"));
136    }
137
138    #[test]
139    fn error_display_duplicate_delegation() {
140        let e = AuthorityError::DuplicateDelegation { id: "abc".into() };
141        assert!(e.to_string().contains("abc"));
142    }
143
144    #[test]
145    fn error_display_circular() {
146        let e = AuthorityError::CircularDelegation("A->B->A".into());
147        assert!(e.to_string().contains("A->B->A"));
148    }
149
150    #[test]
151    fn error_display_not_found() {
152        let e = AuthorityError::NotFound("link-x".into());
153        assert!(e.to_string().contains("link-x"));
154    }
155
156    #[test]
157    fn error_display_empty_chain() {
158        let e = AuthorityError::EmptyChain;
159        assert!(e.to_string().contains("empty"));
160    }
161
162    #[test]
163    fn error_display_permission_denied() {
164        let e = AuthorityError::PermissionDenied("write".into());
165        assert!(e.to_string().contains("write"));
166    }
167
168    #[test]
169    fn error_clone_eq() {
170        let e1 = AuthorityError::EmptyChain;
171        assert_eq!(e1.clone(), e1);
172    }
173}