Skip to main content

dig_did/
types.rs

1//! The public type surface of `dig-did` (SPEC §2).
2//!
3//! Two kinds of types live here: the DID/coin types re-exported verbatim from chia-wallet-sdk (the
4//! byte-source-of-truth, INV-4 — this crate never re-defines a puzzle-carrying type), and the small
5//! `dig-did`-owned types that describe an *unsigned* operation result ([`DidSpend`]) and *who* is
6//! authorized to spend a DID ([`Owner`]).
7
8use chia_wallet_sdk::driver::Spend;
9use chia_wallet_sdk::prelude::PublicKey;
10
11// Re-exported from chia-wallet-sdk so consumers of dig-did never need a direct SDK dependency to
12// name the DID it produces. These are the canonical Chia types — dig-did adds no shadow copy.
13pub use chia_protocol::{Bytes32, Coin, CoinSpend};
14pub use chia_puzzle_types::{LineageProof, Proof};
15pub use chia_wallet_sdk::driver::{Did, DidInfo};
16
17/// The result of building a DID operation: the unsigned coin spends plus the recreated child DID.
18///
19/// This is the crate's output contract (INV-3). A `DidSpend` carries NO signature — the consumer
20/// feeds `coin_spends` to [`crate::required_signatures`], signs the reported messages, assembles a
21/// `SpendBundle`, and broadcasts. `child` is the DID as it will exist AFTER the spend confirms
22/// (`None` for a terminal operation such as a melt, which leaves no DID successor).
23#[derive(Debug, Clone)]
24#[must_use]
25pub struct DidSpend {
26    /// The unsigned coin spends this operation produces, in spend order.
27    pub coin_spends: Vec<CoinSpend>,
28
29    /// The DID as it will exist after these spends confirm, or `None` for a terminal operation.
30    pub child: Option<Did>,
31}
32
33impl DidSpend {
34    /// Creates a [`DidSpend`] from its coin spends and (optional) recreated child DID.
35    pub fn new(coin_spends: Vec<CoinSpend>, child: Option<Did>) -> Self {
36        Self { coin_spends, child }
37    }
38}
39
40/// Who is authorized to spend a DID — i.e. the p2 ("inner") puzzle that guards it.
41///
42/// Every DID operation is authorized by spending the DID's inner puzzle. `Owner` lets a caller pick
43/// that inner puzzle without dig-did hard-coding one:
44///
45/// - [`Owner::Standard`] is the common case — the standard single-key p2 puzzle. dig-did builds the
46///   `StandardLayer` for you; the resulting spend requires one `AGG_SIG_ME` over the given key.
47/// - [`Owner::Custom`] is the escape hatch — the caller supplies an already-built inner [`Spend`]
48///   (any p2 puzzle: a custom vault, a multisig, a delegated puzzle). dig-did passes it through
49///   unchanged, so the caller owns its signature requirements.
50///
51/// **Operations that add conditions of their own** (`create_did`, `create_simple_did`,
52/// `create_eve_did_only`, `spend_did_with_conditions`) require [`Owner::Standard`] and return
53/// [`DidError::UnsupportedOwner`] for [`Owner::Custom`]. A pre-built inner spend emits one fixed
54/// condition set and cannot carry the launcher or recreation conditions computed inside those calls.
55/// See SPEC §2.4.
56#[derive(Debug, Clone, Copy)]
57pub enum Owner {
58    /// The standard single-key p2 puzzle, owned by the given (synthetic) public key.
59    Standard(PublicKey),
60
61    /// A fully pre-built inner spend for a custom p2 puzzle, passed through unchanged.
62    Custom(Spend),
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn did_spend_carries_its_coin_spends_and_child() {
71        let spend = DidSpend::new(Vec::new(), None);
72        assert!(spend.coin_spends.is_empty());
73        assert!(spend.child.is_none());
74    }
75
76    #[test]
77    fn owner_standard_holds_the_given_key() {
78        let key = PublicKey::default();
79        let owner = Owner::Standard(key);
80        match owner {
81            Owner::Standard(k) => assert_eq!(k, key),
82            Owner::Custom(_) => panic!("expected a standard owner"),
83        }
84    }
85}