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}