chia_sdk_driver/layers/
singleton_layer.rs

1use chia_protocol::{Bytes32, Coin};
2use chia_puzzle_types::{
3    singleton::{SingletonArgs, SingletonSolution, SingletonStruct},
4    LineageProof,
5};
6use chia_puzzles::{SINGLETON_LAUNCHER_HASH, SINGLETON_TOP_LAYER_V1_1_HASH};
7use clvm_traits::FromClvm;
8use clvm_utils::{ToTreeHash, TreeHash};
9use clvmr::{Allocator, NodePtr};
10
11use crate::{DriverError, Layer, Puzzle, SpendContext};
12
13/// The singleton [`Layer`] enforces uniqueness on a coin, which is identified by the launcher id.
14/// It contains an inner puzzle layer, which determines the actual behavior of the coin.
15/// Only one singleton can be created when the coin is spent, preserving the lineage of the asset.
16///
17/// Examples of singletons include:
18/// * [`DidLayer`](crate::DidLayer) for Decentralized Identifiers (DIDs).
19/// * [`NftStateLayer`](crate::NftStateLayer) for Non-Fungible Tokens (NFTs).
20///
21/// However, assets like CATs (Chia Asset Tokens) are not singletons, as they are fungible.
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub struct SingletonLayer<I> {
24    /// The unique launcher id for the singleton. Also referred to as the singleton id.
25    pub launcher_id: Bytes32,
26    /// The inner puzzle layer. For singletons, this determines the actual behavior of the coin.
27    pub inner_puzzle: I,
28}
29
30impl<I> SingletonLayer<I> {
31    pub fn new(launcher_id: Bytes32, inner_puzzle: I) -> Self {
32        Self {
33            launcher_id,
34            inner_puzzle,
35        }
36    }
37}
38
39impl<I> Layer for SingletonLayer<I>
40where
41    I: Layer,
42{
43    type Solution = SingletonSolution<I::Solution>;
44
45    fn parse_puzzle(allocator: &Allocator, puzzle: Puzzle) -> Result<Option<Self>, DriverError> {
46        let Some(puzzle) = puzzle.as_curried() else {
47            return Ok(None);
48        };
49
50        if puzzle.mod_hash != SINGLETON_TOP_LAYER_V1_1_HASH.into() {
51            return Ok(None);
52        }
53
54        let args = SingletonArgs::<NodePtr>::from_clvm(allocator, puzzle.args)?;
55
56        if args.singleton_struct.mod_hash != SINGLETON_TOP_LAYER_V1_1_HASH.into()
57            || args.singleton_struct.launcher_puzzle_hash != SINGLETON_LAUNCHER_HASH.into()
58        {
59            return Err(DriverError::InvalidSingletonStruct);
60        }
61
62        let Some(inner_puzzle) =
63            I::parse_puzzle(allocator, Puzzle::parse(allocator, args.inner_puzzle))?
64        else {
65            return Ok(None);
66        };
67
68        Ok(Some(Self {
69            launcher_id: args.singleton_struct.launcher_id,
70            inner_puzzle,
71        }))
72    }
73
74    fn parse_solution(
75        allocator: &Allocator,
76        solution: NodePtr,
77    ) -> Result<Self::Solution, DriverError> {
78        let solution = SingletonSolution::<NodePtr>::from_clvm(allocator, solution)?;
79        let inner_solution = I::parse_solution(allocator, solution.inner_solution)?;
80        Ok(SingletonSolution {
81            lineage_proof: solution.lineage_proof,
82            amount: solution.amount,
83            inner_solution,
84        })
85    }
86
87    fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
88        let inner_puzzle = self.inner_puzzle.construct_puzzle(ctx)?;
89        ctx.curry(SingletonArgs {
90            singleton_struct: SingletonStruct::new(self.launcher_id),
91            inner_puzzle,
92        })
93    }
94
95    fn construct_solution(
96        &self,
97        ctx: &mut SpendContext,
98        solution: Self::Solution,
99    ) -> Result<NodePtr, DriverError> {
100        let inner_solution = self
101            .inner_puzzle
102            .construct_solution(ctx, solution.inner_solution)?;
103        ctx.alloc(&SingletonSolution {
104            lineage_proof: solution.lineage_proof,
105            amount: solution.amount,
106            inner_solution,
107        })
108    }
109}
110
111impl<I> ToTreeHash for SingletonLayer<I>
112where
113    I: ToTreeHash,
114{
115    fn tree_hash(&self) -> TreeHash {
116        let inner_puzzle = self.inner_puzzle.tree_hash();
117        SingletonArgs {
118            singleton_struct: SingletonStruct::new(self.launcher_id),
119            inner_puzzle,
120        }
121        .tree_hash()
122    }
123}
124
125impl<I> SingletonLayer<I>
126where
127    I: ToTreeHash,
128{
129    /// Returns the [`LineageProof`] for this singleton's child.
130    pub fn lineage_proof(&self, this_coin: Coin) -> LineageProof {
131        LineageProof {
132            parent_parent_coin_info: this_coin.parent_coin_info,
133            parent_inner_puzzle_hash: self.inner_puzzle.tree_hash().into(),
134            parent_amount: this_coin.amount,
135        }
136    }
137}