chia_sdk_driver/primitives/datalayer/
datastore_info.rs1use crate::{
2 DelegationLayer, DriverError, Layer, NftStateLayer, OracleLayer, SingletonLayer, SpendContext,
3};
4use chia_protocol::{Bytes, Bytes32};
5use chia_puzzle_types::nft::NftStateLayerArgs;
6use chia_sdk_types::{
7 puzzles::{
8 DelegationLayerArgs, WriterLayerArgs, DELEGATION_LAYER_PUZZLE_HASH,
9 DL_METADATA_UPDATER_PUZZLE_HASH,
10 },
11 MerkleTree,
12};
13use clvm_traits::{ClvmDecoder, ClvmEncoder, FromClvm, FromClvmError, Raw, ToClvm, ToClvmError};
14use clvm_utils::{CurriedProgram, ToTreeHash, TreeHash};
15use clvmr::Allocator;
16use num_bigint::BigInt;
17
18pub type StandardDataStoreLayers<M = DataStoreMetadata, I = DelegationLayer> =
19 SingletonLayer<NftStateLayer<M, I>>;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, ToClvm, FromClvm)]
22#[repr(u8)]
23#[clvm(atom)]
24pub enum HintType {
25 AdminPuzzle = 1,
27 WriterPuzzle = 2,
28 OraclePuzzle = 3,
29}
30
31impl HintType {
32 pub fn from_value(value: u8) -> Option<Self> {
33 match value {
34 1 => Some(Self::AdminPuzzle),
35 2 => Some(Self::WriterPuzzle),
36 3 => Some(Self::OraclePuzzle),
37 _ => None,
38 }
39 }
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Copy)]
43pub enum DelegatedPuzzle {
44 Admin(TreeHash), Writer(TreeHash), Oracle(Bytes32, u64), }
48
49impl DelegatedPuzzle {
50 pub fn from_memos(remaining_memos: &mut Vec<Bytes>) -> Result<Self, DriverError> {
51 if remaining_memos.len() < 2 {
52 return Err(DriverError::MissingMemo);
53 }
54
55 let first_memo = remaining_memos.remove(0);
56 if first_memo.len() != 1 {
57 return Err(DriverError::InvalidMemo);
58 }
59 let puzzle_type = HintType::from_value(first_memo[0]);
60
61 let puzzle_hash: TreeHash = TreeHash::new(
63 remaining_memos
64 .remove(0)
65 .to_vec()
66 .try_into()
67 .map_err(|_| DriverError::InvalidMemo)?,
68 );
69
70 match puzzle_type {
71 Some(HintType::AdminPuzzle) => Ok(DelegatedPuzzle::Admin(puzzle_hash)),
72 Some(HintType::WriterPuzzle) => Ok(DelegatedPuzzle::Writer(puzzle_hash)),
73 Some(HintType::OraclePuzzle) => {
74 if remaining_memos.is_empty() {
75 return Err(DriverError::MissingMemo);
76 }
77
78 let oracle_fee: u64 = BigInt::from_signed_bytes_be(&remaining_memos.remove(0))
80 .to_u64_digits()
81 .1[0];
82
83 Ok(DelegatedPuzzle::Oracle(puzzle_hash.into(), oracle_fee))
84 }
85 None => Err(DriverError::MissingMemo),
86 }
87 }
88}
89
90pub trait MetadataWithRootHash {
91 fn root_hash(&self) -> Bytes32;
92 fn root_hash_only(root_hash: Bytes32) -> Self;
93}
94
95impl MetadataWithRootHash for DataStoreMetadata {
96 fn root_hash(&self) -> Bytes32 {
97 self.root_hash
98 }
99
100 fn root_hash_only(root_hash: Bytes32) -> Self {
101 Self {
102 root_hash,
103 label: None,
104 description: None,
105 bytes: None,
106 }
107 }
108}
109
110#[derive(Debug, Clone, PartialEq, Eq, Default)]
111pub struct DataStoreMetadata {
112 pub root_hash: Bytes32,
113 pub label: Option<String>,
114 pub description: Option<String>,
115 pub bytes: Option<u64>,
116}
117
118impl<N, D: ClvmDecoder<Node = N>> FromClvm<D> for DataStoreMetadata {
119 fn from_clvm(decoder: &D, node: N) -> Result<Self, FromClvmError> {
120 let (root_hash, items) = <(Bytes32, Vec<(String, Raw<N>)>)>::from_clvm(decoder, node)?;
121 let mut metadata = Self::root_hash_only(root_hash);
122
123 for (key, Raw(ptr)) in items {
124 match key.as_str() {
125 "l" => metadata.label = Some(String::from_clvm(decoder, ptr)?),
126 "d" => metadata.description = Some(String::from_clvm(decoder, ptr)?),
127 "b" => metadata.bytes = Some(u64::from_clvm(decoder, ptr)?),
128 _ => (),
129 }
130 }
131
132 Ok(metadata)
133 }
134}
135
136impl<N, E: ClvmEncoder<Node = N>> ToClvm<E> for DataStoreMetadata {
137 fn to_clvm(&self, encoder: &mut E) -> Result<N, ToClvmError> {
138 let mut items: Vec<(&str, Raw<N>)> = Vec::new();
139
140 if let Some(label) = &self.label {
141 items.push(("l", Raw(label.to_clvm(encoder)?)));
142 }
143
144 if let Some(description) = &self.description {
145 items.push(("d", Raw(description.to_clvm(encoder)?)));
146 }
147
148 if let Some(bytes) = self.bytes {
149 items.push(("b", Raw(bytes.to_clvm(encoder)?)));
150 }
151
152 (self.root_hash, items).to_clvm(encoder)
153 }
154}
155
156#[must_use]
157#[derive(Debug, Clone, PartialEq, Eq)]
158pub struct DataStoreInfo<M = DataStoreMetadata> {
159 pub launcher_id: Bytes32,
160 pub metadata: M,
161 pub owner_puzzle_hash: Bytes32,
162 pub delegated_puzzles: Vec<DelegatedPuzzle>,
163}
164
165impl<M> DataStoreInfo<M> {
166 pub fn new(
167 launcher_id: Bytes32,
168 metadata: M,
169 owner_puzzle_hash: Bytes32,
170 delegated_puzzles: Vec<DelegatedPuzzle>,
171 ) -> Self {
172 Self {
173 launcher_id,
174 metadata,
175 owner_puzzle_hash,
176 delegated_puzzles,
177 }
178 }
179
180 pub fn from_layers_with_delegation_layer(
181 layers: StandardDataStoreLayers<M, DelegationLayer>,
182 delegated_puzzles: Vec<DelegatedPuzzle>,
183 ) -> Self {
184 Self {
185 launcher_id: layers.launcher_id,
186 metadata: layers.inner_puzzle.metadata,
187 owner_puzzle_hash: layers.inner_puzzle.inner_puzzle.owner_puzzle_hash,
188 delegated_puzzles,
189 }
190 }
191
192 pub fn from_layers_without_delegation_layer<I>(layers: StandardDataStoreLayers<M, I>) -> Self
193 where
194 I: ToTreeHash,
195 {
196 Self {
197 launcher_id: layers.launcher_id,
198 metadata: layers.inner_puzzle.metadata,
199 owner_puzzle_hash: layers.inner_puzzle.inner_puzzle.tree_hash().into(),
200 delegated_puzzles: vec![],
201 }
202 }
203
204 pub fn into_layers_with_delegation_layer(
205 self,
206 ctx: &mut SpendContext,
207 ) -> Result<StandardDataStoreLayers<M, DelegationLayer>, DriverError> {
208 Ok(SingletonLayer::new(
209 self.launcher_id,
210 NftStateLayer::new(
211 self.metadata,
212 DL_METADATA_UPDATER_PUZZLE_HASH.into(),
213 DelegationLayer::new(
214 self.launcher_id,
215 self.owner_puzzle_hash,
216 get_merkle_tree(ctx, self.delegated_puzzles)?.root(),
217 ),
218 ),
219 ))
220 }
221
222 #[must_use]
223 pub fn into_layers_without_delegation_layer<I>(
224 self,
225 innermost_layer: I,
226 ) -> StandardDataStoreLayers<M, I> {
227 SingletonLayer::new(
228 self.launcher_id,
229 NftStateLayer::new(
230 self.metadata,
231 DL_METADATA_UPDATER_PUZZLE_HASH.into(),
232 innermost_layer,
233 ),
234 )
235 }
236
237 pub fn inner_puzzle_hash(&self, ctx: &mut SpendContext) -> Result<TreeHash, DriverError>
238 where
239 M: ToClvm<Allocator>,
240 {
241 let metadata_ptr = ctx.alloc(&self.metadata)?;
242
243 if !self.delegated_puzzles.is_empty() {
244 return Ok(NftStateLayerArgs::curry_tree_hash(
245 ctx.tree_hash(metadata_ptr),
246 CurriedProgram {
247 program: DELEGATION_LAYER_PUZZLE_HASH,
248 args: DelegationLayerArgs {
249 mod_hash: DELEGATION_LAYER_PUZZLE_HASH.into(),
250 launcher_id: self.launcher_id,
251 owner_puzzle_hash: self.owner_puzzle_hash,
252 merkle_root: get_merkle_tree(ctx, self.delegated_puzzles.clone())?.root(),
253 },
254 }
255 .tree_hash(),
256 ));
257 }
258
259 let inner_ph_hash: TreeHash = self.owner_puzzle_hash.into();
260 Ok(NftStateLayerArgs::curry_tree_hash(
261 ctx.tree_hash(metadata_ptr),
262 inner_ph_hash,
263 ))
264 }
265}
266
267pub fn get_merkle_tree(
268 ctx: &mut SpendContext,
269 delegated_puzzles: Vec<DelegatedPuzzle>,
270) -> Result<MerkleTree, DriverError> {
271 let mut leaves = Vec::<Bytes32>::with_capacity(delegated_puzzles.len());
272
273 for dp in delegated_puzzles {
274 match dp {
275 DelegatedPuzzle::Admin(puzzle_hash) => {
276 leaves.push(puzzle_hash.into());
277 }
278 DelegatedPuzzle::Writer(inner_puzzle_hash) => {
279 leaves.push(WriterLayerArgs::curry_tree_hash(inner_puzzle_hash).into());
280 }
281 DelegatedPuzzle::Oracle(oracle_puzzle_hash, oracle_fee) => {
282 let oracle_full_puzzle_ptr = OracleLayer::new(oracle_puzzle_hash, oracle_fee)
283 .ok_or(DriverError::OddOracleFee)?
284 .construct_puzzle(ctx)?;
285
286 leaves.push(ctx.tree_hash(oracle_full_puzzle_ptr).into());
287 }
288 }
289 }
290
291 Ok(MerkleTree::new(&leaves))
292}