chia_sdk_driver/layers/
singleton_layer.rs1use 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub struct SingletonLayer<I> {
24 pub launcher_id: Bytes32,
26 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 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}