chia_sdk_driver/layers/
did_layer.rs

1use 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/// The DID [`Layer`] keeps track of metadata and handles recovery capabilities.
14/// It's typically an inner layer of the [`SingletonLayer`](crate::SingletonLayer).
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub struct DidLayer<M, I> {
17    /// The unique launcher id for the DID. Also referred to as the DID id.
18    pub launcher_id: Bytes32,
19    /// The tree hash of an optional list of recovery DIDs.
20    pub recovery_list_hash: Option<Bytes32>,
21    /// The number of verifications required to recover the DID.
22    pub num_verifications_required: u64,
23    /// Metadata associated with the DID. This is often just `()` for DIDs without metadata.
24    pub metadata: M,
25    /// The inner puzzle layer, commonly used for determining ownership.
26    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}