chia_sdk_driver/primitives/
singleton.rs

1use chia_protocol::{Bytes32, Coin};
2use chia_puzzle_types::{singleton::SingletonArgs, LineageProof, Proof};
3use clvm_utils::TreeHash;
4
5/// A generic singleton primitive, which can be extended with the [`SingletonInfo`] trait.
6#[must_use]
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub struct Singleton<I> {
9    /// The coin that this [`Singleton`] represents. Its puzzle hash should match the singleton outer puzzle hash.
10    pub coin: Coin,
11
12    /// The proof is needed by the singleton puzzle to prove that this coin is a legitimate singleton.
13    /// It's typically obtained by looking up and parsing the parent coin.
14    ///
15    /// Note that while the proof will be a [`LineageProof`] for most coins,
16    /// for the first singleton in the lineage it will be an [`EveProof`](chia_puzzle_types::EveProof) instead.
17    /// However, the eve coin is typically unhinted and spent in the same transaction as it was created,
18    /// so this is not relevant for database storage or syncing unspent coins.
19    pub proof: Proof,
20
21    /// The information needed to construct the outer puzzle.
22    pub info: I,
23}
24
25impl<I> Singleton<I>
26where
27    I: SingletonInfo,
28{
29    pub fn new(coin: Coin, proof: Proof, info: I) -> Self {
30        Self { coin, proof, info }
31    }
32
33    /// Creates a [`LineageProof`] for which would be valid for any children created by this [`Singleton`].
34    pub fn child_lineage_proof(&self) -> LineageProof {
35        LineageProof {
36            parent_parent_coin_info: self.coin.parent_coin_info,
37            parent_inner_puzzle_hash: self.info.inner_puzzle_hash().into(),
38            parent_amount: self.coin.amount,
39        }
40    }
41
42    /// Creates a new [`Singleton`] that represents a child of this one.
43    ///
44    /// You can specify the new [`SingletonInfo`] to use for the child.
45    ///
46    /// It's important to use the right [`SingletonInfo`] instead of modifying it afterward,
47    /// otherwise the puzzle hash of the child will not match the one expected by the coin.
48    pub fn child_with<N>(&self, info: N, amount: u64) -> Singleton<N>
49    where
50        N: SingletonInfo,
51    {
52        Singleton::new(
53            Coin::new(
54                self.coin.coin_id(),
55                SingletonArgs::curry_tree_hash(info.launcher_id(), info.inner_puzzle_hash()).into(),
56                amount,
57            ),
58            Proof::Lineage(self.child_lineage_proof()),
59            info,
60        )
61    }
62}
63
64pub trait SingletonInfo {
65    fn launcher_id(&self) -> Bytes32;
66
67    /// Calculates the inner puzzle hash of the singleton.
68    ///
69    /// This does not include the [`SingletonLayer`](crate::SingletonLayer).
70    fn inner_puzzle_hash(&self) -> TreeHash;
71
72    /// Calculates the full puzzle hash of the the outer [`SingletonLayer`](crate::SingletonLayer).
73    fn puzzle_hash(&self) -> TreeHash {
74        SingletonArgs::curry_tree_hash(self.launcher_id(), self.inner_puzzle_hash())
75    }
76}