#[non_exhaustive]pub enum DidError {
Show 20 variants
Driver(DriverError),
Signer(String),
Parse(String),
NotDid,
InvalidDidString(String),
UnsupportedOwner(&'static str),
EvenSingletonAmount(u64),
OddAmountCreateCoin,
AggSigUnsafeInConditions,
NonCanonicalCreateCoinAmount(String),
DisallowedCondition(String),
InvalidRecovery(String),
MissingLineage,
MissingHint,
Chain(String),
NoIdentitySingleton,
NotASingleton,
NotDidRooted,
LauncherMismatch,
LineageTooDeep,
}Expand description
Everything that can go wrong while building or parsing a DID spend.
The variants split into two families: errors delegated to the chia-wallet-sdk driver/signer (wrapped verbatim so the underlying cause is never lost), and DID-domain errors this crate raises itself (parse/hydration/codec guards, all fail-closed per SPEC §5).
Marked #[non_exhaustive]: this taxonomy grows whenever a new fail-closed guard is added, and
every such addition would otherwise be a breaking change for any downstream exhaustive match.
Downstream code must carry a _ arm. dig-account’s AccountError is #[non_exhaustive] for
the same reason; the two now agree.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
Driver(DriverError)
A chia-wallet-sdk driver operation failed (puzzle currying, spend construction, CLVM
evaluation). The wrapped DriverError carries the precise cause.
Signer(String)
The signing calculator failed to derive the required signatures from the coin spends
(invalid puzzle/solution, an infinity public key in an AGG_SIG condition). The message is
the underlying signer error rendered as a string, so this crate does not leak the signer’s
error type into its public surface.
Parse(String)
A coin/puzzle/solution could not be parsed as the expected shape.
NotDid
The supplied puzzle parsed successfully but is not a DID singleton.
InvalidDidString(String)
A did:chia:1… string was malformed or failed bech32m decoding.
UnsupportedOwner(&'static str)
The operation cannot honour the crate::Owner variant it was given, because it must add
conditions of its own and a caller-supplied pre-built inner spend emits one fixed condition
set. Rather than silently dropping those conditions — which yields a well-formed bundle that
creates none of the coins it reports — the operation refuses. The message names the
alternative the caller should use instead (SPEC §5, fail-closed).
EvenSingletonAmount(u64)
A funding coin with an EVEN amount was supplied to a DID launch. The u64 is that amount.
Chia’s singleton top layer recognises only the launcher’s ODD-amount output as the singleton, and this crate’s launch gives the singleton the funding coin’s entire amount. An even-amount funding coin therefore produces a bundle that spends the money and creates no DID at all — a total, silent loss of the funding coin, not a rejected spend. Arbitrary wallet coins are even about half the time.
Split the funding coin down to exactly the odd amount the singleton should carry first
(dig-account splits to 1 mojo) and pass that coin (SPEC §3, fail-closed).
OddAmountCreateCoin
A caller supplied an odd-amount CREATE_COIN to a DID-preserving spend. A singleton’s inner
puzzle may emit exactly ONE odd-amount CREATE_COIN, and the DID’s own recreation occupies
it, so a caller’s odd-amount CREATE_COIN can never be valid here — most often an attempt to
parent a foreign singleton launcher (an amount-1 coin) to the DID coin.
Refused at build time because the alternative is opaque: the bundle would assemble and report a child DID, then be rejected at mempool admission. It never enters a block, so no fee is paid — but the caller pays a wasted round-trip and gets no explanation. Parent the launcher to an ordinary coin instead and bind it to the DID by an announcement this spend asserts, or by the launched singleton’s owner puzzle hash (SPEC §5, fail-closed).
AggSigUnsafeInConditions
A caller supplied an AGG_SIG_UNSAFE requirement in the conditions of a DID spend.
Unlike every other AGG_SIG_* condition, AGG_SIG_UNSAFE is signed with no coin binding
and no domain separation — the signed message is the caller’s bytes verbatim. A DID owner
induced to sign one produces a permanent, replayable assertion under their identity key,
reusable in any spend or challenge-response the attacker later constructs. Since this crate’s
contract is that the caller signs every message required_signatures reports, such a
requirement is never legitimate in a DID spend and is refused (SPEC §5, fail-closed).
This refusal removes the UNBOUNDED shape, not every shape whose damage outlives the bundle.
A permitted AGG_SIG_PARENT also outlives it: that signature is bound to the DID coin’s
PARENT id, so it stays satisfiable by any future spend of any coin sharing that parent — the
other outputs of the DID’s PREVIOUS spend, not anything this spend creates. That set was
fixed before this spend was built and MAY include a coin an earlier caller paid to a third
party, under a puzzle that third party chose. What the refusal buys is a BOUND, not an end
to persistence: unlike AGG_SIG_UNSAFE, a permitted signature can never reach a later
generation of the DID and can never become an off-domain assertion.
Nor does it make a hostile condition set safe. The permitted shapes still move the caller’s
own bundled funds to caller-chosen puzzle hashes and still emit announcements under the
DID’s authority. A caller composing conditions from an untrusted source MUST review the
bundle before signing — and, where an AGG_SIG_PARENT is present, MUST also account for
what the DID’s PREVIOUS spend created, which this bundle does not show.
NonCanonicalCreateCoinAmount(String)
A caller supplied a CREATE_COIN whose amount atom is not chia’s canonical integer encoding.
CLVM integers are SIGNED and chia additionally requires a canonical encoding, but the typed
CreateCoin::amount this crate’s allowlist reads is a u64 decoded from the atom UNSIGNED.
The two disagree on exactly the encodings chia refuses: a leading byte with the sign bit set
(0x80 reads as 128, chain says CoinAmountNegative), a redundant leading zero (0x000002
reads as 2, chain says InvalidCoinAmount), and an atom with more bytes than the value needs
(chain says the amount overflows). Such a spend assembles here, reports a child DID, and is
then dropped at mempool admission telling the caller nothing — the opaque failure this guard
exists to prevent.
The rule mirrors chia’s sanitize_uint exactly, so it can refuse nothing the chain would
accept (SPEC §5, fail-closed).
DisallowedCondition(String)
A caller supplied a condition that is not on the allowlist of shapes a DID-preserving spend may carry. The string renders the offending condition.
The guard is an allowlist rather than a list of refusals for a structural reason:
chia_sdk_types::Condition is #[non_exhaustive] and carries a catch-all Other variant
that serializes to CLVM verbatim, so any caller can hand a refused condition over under a
name a denylist does not recognise while the chain still sees the condition itself. Only a
guard that refuses everything it does not explicitly permit can fail closed — and it stays
closed when a future SDK release adds a variant nobody here has considered (SPEC §5).
InvalidRecovery(String)
A recovery operation supplied an inconsistent recovery configuration (list hash / required verifications mismatch).
MissingLineage
Hydration could not establish the lineage proof required to spend the DID (SPEC §5, fail-closed).
MissingHint
A parsed DID coin was missing the owner hint memo required to recreate its child (SPEC §5, fail-closed).
Chain(String)
A chain-level precondition was violated (e.g. a supplied coin does not match the expected
launcher). The string states the specific violation. Also carries a crate::resolve::ChainSource
read error verbatim — a failed read NEVER degrades to “assume owned” (SPEC §5, fail-closed).
NoIdentitySingleton
The DID’s identity singleton has no current on-chain coin — it was never launched, or has been melted, so there is no lineage to root a coin against (SPEC §5, fail-closed).
NotASingleton
The coin under proof could not be authenticated as a genuine singleton: its parent-spend chain does not resolve to a singleton launcher (an ordinary payment/change coin, or a pay-to coin that merely wears a singleton puzzle hash without a genuine recreation parent spend). SPEC §5.
NotDidRooted
The coin authenticates as a genuine singleton, but neither IS the DID singleton nor was launched from a coin in the DID singleton’s lineage — it is not rooted in the DID’s identity (SPEC §5).
LauncherMismatch
The DID’s current tip authenticated as a genuine singleton, but its GENUINE launcher (walked
from the parent-spend chain) is not the launcher that was requested. This is the money-critical
guard for crate::resolve_xch_address: a dishonest crate::ChainSource can echo an
attacker DID’s tip for a victim launcher, and the curried launcher_id on that tip is
attacker-chosen, so only the parent-walk-authenticated launcher may be trusted. Resolving an
address from a mismatched launcher would pay the wrong recipient, so this fails closed (SPEC §5).
LineageTooDeep
The parent-spend walk exceeded crate::resolve::MAX_LINEAGE_DEPTH — a DoS guard against an
unbounded (possibly adversarial) lineage. The proof fails closed rather than walk forever.
Trait Implementations§
Source§impl Error for DidError
impl Error for DidError
Source§fn source(&self) -> Option<&(dyn Error + 'static)>
fn source(&self) -> Option<&(dyn Error + 'static)>
1.0.0 · Source§fn description(&self) -> &str
fn description(&self) -> &str
use the Display impl or to_string()
Source§impl From<DriverError> for DidError
impl From<DriverError> for DidError
Source§fn from(source: DriverError) -> Self
fn from(source: DriverError) -> Self
Auto Trait Implementations§
impl !RefUnwindSafe for DidError
impl !UnwindSafe for DidError
impl Freeze for DidError
impl Send for DidError
impl Sync for DidError
impl Unpin for DidError
impl UnsafeUnpin for DidError
Blanket Implementations§
Source§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
Source§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.Source§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.