Skip to main content

dig_evidence/
error.rs

1//! [`EvidenceError`] — the crate's fail-closed error taxonomy (SPEC §6).
2//!
3//! Every gather/verify path fails CLOSED: an unreadable chain, a missing coin, a forged anchor, or a
4//! proof that does not fold is an `Err`, never a silently-accepted absence. The `None`-vs-`Err`
5//! discipline of [`dig_chainsource_interface::ChainSource`] is preserved — a reliable "does not
6//! exist" answer becomes a specific typed variant (e.g. [`EvidenceError::LauncherNotFound`]), while an
7//! "could not answer" transport failure becomes [`EvidenceError::Chain`]. Both stop the proof; neither
8//! is ever treated as "assume valid".
9
10use thiserror::Error;
11
12/// The single error type every [`Evidence`](crate::Evidence) gather/verify returns. Each variant is a
13/// stable, catalogued failure reason (§6.2) so a consumer can branch on WHY a proof could not be
14/// established without parsing prose.
15#[derive(Debug, Clone, PartialEq, Eq, Error)]
16pub enum EvidenceError {
17    /// A [`ChainSource`](dig_chainsource_interface::ChainSource) read could not be answered (transport,
18    /// timeout, malformed, unsupported). The answer is UNKNOWN — the proof fails closed, never
19    /// degrading an unreliable read to "assume valid". Carries the source's `Display` string.
20    #[error("chain read failed: {0}")]
21    Chain(String),
22
23    /// The claimed launcher coin (`store_id`) does not exist per the chain source. The unforgeable
24    /// identity anchor is missing, so no root-anchor proof can be built (fail closed).
25    #[error("launcher coin (store_id) not found on chain")]
26    LauncherNotFound,
27
28    /// A coin was found at `store_id`, but its puzzle hash is NOT the singleton launcher puzzle hash —
29    /// so `store_id` is not a genuine launcher coin. Rejecting this is what makes the launcher-coin
30    /// anchor unforgeable: identity is anchored on the launcher COIN, never a curried `launcher_id`.
31    #[error("coin at store_id is not a singleton launcher")]
32    NotALauncher,
33
34    /// The store's singleton lineage could not be resolved — the launcher never produced a singleton,
35    /// or it has been fully melted. Without an authenticated lineage the claimed root cannot be
36    /// anchored (fail closed).
37    #[error("no singleton lineage for store_id")]
38    NoLineage,
39
40    /// The claimed generation root is not committed by any coin in the store's authenticated lineage
41    /// (within the bounded walk). The root is not genuinely anchored to this store → reject.
42    #[error("generation root is not committed by any coin in the store lineage")]
43    RootNotCommitted,
44
45    /// The backward lineage walk exceeded [`MAX_LINEAGE_DEPTH`](crate::MAX_LINEAGE_DEPTH) without
46    /// resolving the claim. Fails closed rather than walking unboundedly.
47    #[error("store lineage walk exceeded the maximum depth")]
48    LineageTooDeep,
49
50    /// A merkle inclusion path did not fold to the claimed generation root. The supplied leaf is not
51    /// provably contained under that root → reject.
52    #[error("merkle inclusion proof does not fold to the claimed root")]
53    ProofDoesNotFold,
54
55    /// The two roots a composite proof must bind — the merkle-fold root and the chain-anchored root —
56    /// are not the same 32 bytes. The range proof and the anchor proof describe different content, so
57    /// their conjunction is meaningless → reject.
58    #[error("range-inclusion root and chain-anchored root disagree")]
59    RootMismatch,
60}
61
62/// A convenience result alias for evidence gather/verify.
63pub type EvidenceResult<T> = Result<T, EvidenceError>;