chia_sdk_driver/primitives/did/
did_info.rs1use chia_protocol::Bytes32;
2use chia_puzzle_types::{
3 did::DidArgs,
4 singleton::{SingletonArgs, SingletonStruct},
5};
6use chia_sdk_types::Mod;
7use clvm_traits::{FromClvm, ToClvm};
8use clvm_utils::{ToTreeHash, TreeHash};
9use clvmr::Allocator;
10
11use crate::{DidLayer, DriverError, Layer, Puzzle, SingletonLayer};
12
13pub type StandardDidLayers<M, I> = SingletonLayer<DidLayer<M, I>>;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub struct DidInfo<M> {
21 pub launcher_id: Bytes32,
23
24 pub recovery_list_hash: Option<Bytes32>,
31
32 pub num_verifications_required: u64,
34
35 pub metadata: M,
39
40 pub p2_puzzle_hash: Bytes32,
43}
44
45impl<M> DidInfo<M> {
46 pub fn new(
47 launcher_id: Bytes32,
48 recovery_list_hash: Option<Bytes32>,
49 num_verifications_required: u64,
50 metadata: M,
51 p2_puzzle_hash: Bytes32,
52 ) -> Self {
53 Self {
54 launcher_id,
55 recovery_list_hash,
56 num_verifications_required,
57 metadata,
58 p2_puzzle_hash,
59 }
60 }
61
62 pub fn with_metadata<N>(self, metadata: N) -> DidInfo<N> {
63 DidInfo {
64 launcher_id: self.launcher_id,
65 recovery_list_hash: self.recovery_list_hash,
66 num_verifications_required: self.num_verifications_required,
67 metadata,
68 p2_puzzle_hash: self.p2_puzzle_hash,
69 }
70 }
71
72 pub fn parse(
79 allocator: &Allocator,
80 puzzle: Puzzle,
81 ) -> Result<Option<(Self, Puzzle)>, DriverError>
82 where
83 M: ToClvm<Allocator> + FromClvm<Allocator>,
84 {
85 let Some(layers) = StandardDidLayers::<M, Puzzle>::parse_puzzle(allocator, puzzle)? else {
86 return Ok(None);
87 };
88
89 let p2_puzzle = layers.inner_puzzle.inner_puzzle;
90
91 Ok(Some((Self::from_layers(layers), p2_puzzle)))
92 }
93
94 pub fn from_layers<I>(layers: StandardDidLayers<M, I>) -> Self
95 where
96 I: ToTreeHash,
97 {
98 Self {
99 launcher_id: layers.launcher_id,
100 recovery_list_hash: layers.inner_puzzle.recovery_list_hash,
101 num_verifications_required: layers.inner_puzzle.num_verifications_required,
102 metadata: layers.inner_puzzle.metadata,
103 p2_puzzle_hash: layers.inner_puzzle.inner_puzzle.tree_hash().into(),
104 }
105 }
106
107 #[must_use]
108 pub fn into_layers<I>(self, p2_puzzle: I) -> StandardDidLayers<M, I> {
109 SingletonLayer::new(
110 self.launcher_id,
111 DidLayer::new(
112 self.launcher_id,
113 self.recovery_list_hash,
114 self.num_verifications_required,
115 self.metadata,
116 p2_puzzle,
117 ),
118 )
119 }
120
121 pub fn inner_puzzle_hash(&self) -> TreeHash
125 where
126 M: ToTreeHash,
127 {
128 DidArgs::curry_tree_hash(
129 self.p2_puzzle_hash.into(),
130 self.recovery_list_hash,
131 self.num_verifications_required,
132 SingletonStruct::new(self.launcher_id),
133 self.metadata.tree_hash(),
134 )
135 }
136
137 pub fn puzzle_hash(&self) -> TreeHash
139 where
140 M: ToTreeHash,
141 {
142 SingletonArgs::new(self.launcher_id, self.inner_puzzle_hash()).curry_tree_hash()
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use chia_sdk_test::Simulator;
149 use chia_sdk_types::Conditions;
150
151 use crate::{Launcher, SpendContext, StandardLayer};
152
153 use super::*;
154
155 #[test]
156 fn test_parse_did_info() -> anyhow::Result<()> {
157 let mut sim = Simulator::new();
158 let ctx = &mut SpendContext::new();
159
160 let alice = sim.bls(1);
161 let alice_p2 = StandardLayer::new(alice.pk);
162
163 let custom_metadata = ["Metadata".to_string(), "Example".to_string()];
164 let (create_did, did) = Launcher::new(alice.coin.coin_id(), 1).create_did(
165 ctx,
166 None,
167 1,
168 custom_metadata,
169 &alice_p2,
170 )?;
171 alice_p2.spend(ctx, alice.coin, create_did)?;
172
173 let original_did = did.clone();
174 let _did = did.update(ctx, &alice_p2, Conditions::new())?;
175
176 sim.spend_coins(ctx.take(), &[alice.sk])?;
177
178 let puzzle_reveal = sim
179 .puzzle_reveal(original_did.coin.coin_id())
180 .expect("missing did puzzle");
181
182 let mut allocator = Allocator::new();
183 let ptr = puzzle_reveal.to_clvm(&mut allocator)?;
184 let puzzle = Puzzle::parse(&allocator, ptr);
185 let (did_info, p2_puzzle) =
186 DidInfo::<[String; 2]>::parse(&allocator, puzzle)?.expect("not a did");
187
188 assert_eq!(did_info, original_did.info);
189 assert_eq!(p2_puzzle.curried_puzzle_hash(), alice.puzzle_hash.into());
190
191 Ok(())
192 }
193}