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 MerkleTree,
8 puzzles::{
9 DELEGATION_LAYER_PUZZLE_HASH, DL_METADATA_UPDATER_PUZZLE_HASH, DelegationLayerArgs,
10 WriterLayerArgs,
11 },
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 size_proof: None,
107 }
108 }
109}
110
111#[derive(Debug, Clone, PartialEq, Eq, Default)]
112pub struct DataStoreMetadata {
113 pub root_hash: Bytes32,
114 pub label: Option<String>,
115 pub description: Option<String>,
116 pub bytes: Option<u64>,
117 pub size_proof: Option<String>,
118}
119
120impl<N, D: ClvmDecoder<Node = N>> FromClvm<D> for DataStoreMetadata {
121 fn from_clvm(decoder: &D, node: N) -> Result<Self, FromClvmError> {
122 let (root_hash, items) = <(Bytes32, Vec<(String, Raw<N>)>)>::from_clvm(decoder, node)?;
123 let mut metadata = Self::root_hash_only(root_hash);
124
125 for (key, Raw(ptr)) in items {
126 match key.as_str() {
127 "l" => metadata.label = Some(String::from_clvm(decoder, ptr)?),
128 "d" => metadata.description = Some(String::from_clvm(decoder, ptr)?),
129 "b" => metadata.bytes = Some(u64::from_clvm(decoder, ptr)?),
130 "sp" => metadata.size_proof = Some(String::from_clvm(decoder, ptr)?),
131 _ => (),
132 }
133 }
134
135 Ok(metadata)
136 }
137}
138
139impl<N, E: ClvmEncoder<Node = N>> ToClvm<E> for DataStoreMetadata {
140 fn to_clvm(&self, encoder: &mut E) -> Result<N, ToClvmError> {
141 let mut items: Vec<(&str, Raw<N>)> = Vec::new();
142
143 if let Some(label) = &self.label {
144 items.push(("l", Raw(label.to_clvm(encoder)?)));
145 }
146
147 if let Some(description) = &self.description {
148 items.push(("d", Raw(description.to_clvm(encoder)?)));
149 }
150
151 if let Some(bytes) = self.bytes {
152 items.push(("b", Raw(bytes.to_clvm(encoder)?)));
153 }
154
155 if let Some(size_proof) = &self.size_proof {
156 items.push(("sp", Raw(size_proof.to_clvm(encoder)?)));
157 }
158
159 (self.root_hash, items).to_clvm(encoder)
160 }
161}
162
163#[must_use]
164#[derive(Debug, Clone, PartialEq, Eq)]
165pub struct DataStoreInfo<M = DataStoreMetadata> {
166 pub launcher_id: Bytes32,
167 pub metadata: M,
168 pub owner_puzzle_hash: Bytes32,
169 pub delegated_puzzles: Vec<DelegatedPuzzle>,
170}
171
172impl<M> DataStoreInfo<M> {
173 pub fn new(
174 launcher_id: Bytes32,
175 metadata: M,
176 owner_puzzle_hash: Bytes32,
177 delegated_puzzles: Vec<DelegatedPuzzle>,
178 ) -> Self {
179 Self {
180 launcher_id,
181 metadata,
182 owner_puzzle_hash,
183 delegated_puzzles,
184 }
185 }
186
187 pub fn from_layers_with_delegation_layer(
188 layers: StandardDataStoreLayers<M, DelegationLayer>,
189 delegated_puzzles: Vec<DelegatedPuzzle>,
190 ) -> Self {
191 Self {
192 launcher_id: layers.launcher_id,
193 metadata: layers.inner_puzzle.metadata,
194 owner_puzzle_hash: layers.inner_puzzle.inner_puzzle.owner_puzzle_hash,
195 delegated_puzzles,
196 }
197 }
198
199 pub fn from_layers_without_delegation_layer<I>(layers: StandardDataStoreLayers<M, I>) -> Self
200 where
201 I: ToTreeHash,
202 {
203 Self {
204 launcher_id: layers.launcher_id,
205 metadata: layers.inner_puzzle.metadata,
206 owner_puzzle_hash: layers.inner_puzzle.inner_puzzle.tree_hash().into(),
207 delegated_puzzles: vec![],
208 }
209 }
210
211 pub fn into_layers_with_delegation_layer(
212 self,
213 ctx: &mut SpendContext,
214 ) -> Result<StandardDataStoreLayers<M, DelegationLayer>, DriverError> {
215 Ok(SingletonLayer::new(
216 self.launcher_id,
217 NftStateLayer::new(
218 self.metadata,
219 DL_METADATA_UPDATER_PUZZLE_HASH.into(),
220 DelegationLayer::new(
221 self.launcher_id,
222 self.owner_puzzle_hash,
223 get_merkle_tree(ctx, self.delegated_puzzles)?.root(),
224 ),
225 ),
226 ))
227 }
228
229 #[must_use]
230 pub fn into_layers_without_delegation_layer<I>(
231 self,
232 innermost_layer: I,
233 ) -> StandardDataStoreLayers<M, I> {
234 SingletonLayer::new(
235 self.launcher_id,
236 NftStateLayer::new(
237 self.metadata,
238 DL_METADATA_UPDATER_PUZZLE_HASH.into(),
239 innermost_layer,
240 ),
241 )
242 }
243
244 pub fn inner_puzzle_hash(&self, ctx: &mut SpendContext) -> Result<TreeHash, DriverError>
245 where
246 M: ToClvm<Allocator>,
247 {
248 let metadata_ptr = ctx.alloc(&self.metadata)?;
249
250 if !self.delegated_puzzles.is_empty() {
251 return Ok(NftStateLayerArgs::curry_tree_hash(
252 ctx.tree_hash(metadata_ptr),
253 CurriedProgram {
254 program: DELEGATION_LAYER_PUZZLE_HASH,
255 args: DelegationLayerArgs {
256 mod_hash: DELEGATION_LAYER_PUZZLE_HASH.into(),
257 launcher_id: self.launcher_id,
258 owner_puzzle_hash: self.owner_puzzle_hash,
259 merkle_root: get_merkle_tree(ctx, self.delegated_puzzles.clone())?.root(),
260 },
261 }
262 .tree_hash(),
263 ));
264 }
265
266 let inner_ph_hash: TreeHash = self.owner_puzzle_hash.into();
267 Ok(NftStateLayerArgs::curry_tree_hash(
268 ctx.tree_hash(metadata_ptr),
269 inner_ph_hash,
270 ))
271 }
272}
273
274pub fn get_merkle_tree(
275 ctx: &mut SpendContext,
276 delegated_puzzles: Vec<DelegatedPuzzle>,
277) -> Result<MerkleTree, DriverError> {
278 let mut leaves = Vec::<Bytes32>::with_capacity(delegated_puzzles.len());
279
280 for dp in delegated_puzzles {
281 match dp {
282 DelegatedPuzzle::Admin(puzzle_hash) => {
283 leaves.push(puzzle_hash.into());
284 }
285 DelegatedPuzzle::Writer(inner_puzzle_hash) => {
286 leaves.push(WriterLayerArgs::curry_tree_hash(inner_puzzle_hash).into());
287 }
288 DelegatedPuzzle::Oracle(oracle_puzzle_hash, oracle_fee) => {
289 let oracle_full_puzzle_ptr = OracleLayer::new(oracle_puzzle_hash, oracle_fee)
290 .ok_or(DriverError::OddOracleFee)?
291 .construct_puzzle(ctx)?;
292
293 leaves.push(ctx.tree_hash(oracle_full_puzzle_ptr).into());
294 }
295 }
296 }
297
298 Ok(MerkleTree::new(&leaves))
299}