chia_sdk_driver/layers/
did_layer.rs1use chia_protocol::Bytes32;
2use chia_puzzle_types::{
3 did::{DidArgs, DidSolution},
4 singleton::SingletonStruct,
5};
6use chia_puzzles::{DID_INNERPUZ_HASH, SINGLETON_LAUNCHER_HASH, SINGLETON_TOP_LAYER_V1_1_HASH};
7use clvm_traits::{FromClvm, ToClvm};
8use clvm_utils::{ToTreeHash, TreeHash};
9use clvmr::{Allocator, NodePtr};
10
11use crate::{DriverError, Layer, Puzzle, SpendContext};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub struct DidLayer<M, I> {
17 pub launcher_id: Bytes32,
19 pub recovery_list_hash: Option<Bytes32>,
21 pub num_verifications_required: u64,
23 pub metadata: M,
25 pub inner_puzzle: I,
27}
28
29impl<M, I> DidLayer<M, I> {
30 pub fn new(
31 launcher_id: Bytes32,
32 recovery_list_hash: Option<Bytes32>,
33 num_verifications_required: u64,
34 metadata: M,
35 inner_puzzle: I,
36 ) -> Self {
37 Self {
38 launcher_id,
39 recovery_list_hash,
40 num_verifications_required,
41 metadata,
42 inner_puzzle,
43 }
44 }
45
46 pub fn with_metadata<N>(self, metadata: N) -> DidLayer<N, I> {
47 DidLayer {
48 launcher_id: self.launcher_id,
49 recovery_list_hash: self.recovery_list_hash,
50 num_verifications_required: self.num_verifications_required,
51 metadata,
52 inner_puzzle: self.inner_puzzle,
53 }
54 }
55}
56
57impl<M, I> Layer for DidLayer<M, I>
58where
59 I: Layer,
60 M: ToClvm<Allocator> + FromClvm<Allocator>,
61{
62 type Solution = DidSolution<I::Solution>;
63
64 fn parse_puzzle(allocator: &Allocator, puzzle: Puzzle) -> Result<Option<Self>, DriverError> {
65 let Some(puzzle) = puzzle.as_curried() else {
66 return Ok(None);
67 };
68
69 if puzzle.mod_hash != DID_INNERPUZ_HASH.into() {
70 return Ok(None);
71 }
72
73 let args = DidArgs::<NodePtr, M>::from_clvm(allocator, puzzle.args)?;
74
75 if args.singleton_struct.mod_hash != SINGLETON_TOP_LAYER_V1_1_HASH.into()
76 || args.singleton_struct.launcher_puzzle_hash != SINGLETON_LAUNCHER_HASH.into()
77 {
78 return Err(DriverError::InvalidSingletonStruct);
79 }
80
81 let Some(inner_puzzle) =
82 I::parse_puzzle(allocator, Puzzle::parse(allocator, args.inner_puzzle))?
83 else {
84 return Ok(None);
85 };
86
87 Ok(Some(Self {
88 launcher_id: args.singleton_struct.launcher_id,
89 recovery_list_hash: args.recovery_list_hash,
90 num_verifications_required: args.num_verifications_required,
91 metadata: args.metadata,
92 inner_puzzle,
93 }))
94 }
95
96 fn parse_solution(
97 allocator: &Allocator,
98 solution: NodePtr,
99 ) -> Result<Self::Solution, DriverError> {
100 match DidSolution::<NodePtr>::from_clvm(allocator, solution)? {
101 DidSolution::Spend(inner_solution) => {
102 let inner_solution = I::parse_solution(allocator, inner_solution)?;
103 Ok(DidSolution::Spend(inner_solution))
104 }
105 DidSolution::Recover(recovery) => Ok(DidSolution::Recover(recovery)),
106 }
107 }
108
109 fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
110 let inner_puzzle = self.inner_puzzle.construct_puzzle(ctx)?;
111 ctx.curry(DidArgs::new(
112 inner_puzzle,
113 self.recovery_list_hash,
114 self.num_verifications_required,
115 SingletonStruct::new(self.launcher_id),
116 &self.metadata,
117 ))
118 }
119
120 fn construct_solution(
121 &self,
122 ctx: &mut SpendContext,
123 solution: Self::Solution,
124 ) -> Result<NodePtr, DriverError> {
125 match solution {
126 DidSolution::Spend(inner_solution) => {
127 let inner_solution = self.inner_puzzle.construct_solution(ctx, inner_solution)?;
128 Ok(ctx.alloc(&DidSolution::Spend(inner_solution))?)
129 }
130 DidSolution::Recover(recovery) => {
131 Ok(ctx.alloc(&DidSolution::<NodePtr>::Recover(recovery))?)
132 }
133 }
134 }
135}
136
137impl<M, I> ToTreeHash for DidLayer<M, I>
138where
139 M: ToTreeHash,
140 I: ToTreeHash,
141{
142 fn tree_hash(&self) -> TreeHash {
143 let inner_puzzle_hash = self.inner_puzzle.tree_hash();
144 let metadata_hash = self.metadata.tree_hash();
145 DidArgs::curry_tree_hash(
146 inner_puzzle_hash,
147 self.recovery_list_hash,
148 self.num_verifications_required,
149 SingletonStruct::new(self.launcher_id),
150 metadata_hash,
151 )
152 }
153}