chia_sdk_driver/layers/action_layer/
verification_layer.rs1use std::fmt::Debug;
2
3use chia_protocol::Bytes32;
4use chia_puzzle_types::singleton::SingletonStruct;
5use chia_puzzles::{SINGLETON_LAUNCHER_HASH, SINGLETON_TOP_LAYER_V1_1_HASH};
6use chia_sdk_types::puzzles::{
7 CatNftMetadata, VerificationLayer1stCurryArgs, VerificationLayer2ndCurryArgs,
8 VerificationLayerSolution, VERIFICATION_LAYER_PUZZLE_HASH,
9};
10use clvm_traits::{clvm_list, FromClvm, ToClvm};
11use clvm_utils::{CurriedProgram, ToTreeHash};
12use clvmr::{Allocator, NodePtr};
13
14use crate::{DriverError, Layer, Puzzle, SpendContext};
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct VerificationLayer {
18 pub revocation_singleton_launcher_id: Bytes32,
19 pub verified_data: VerifiedData,
20}
21
22impl VerificationLayer {
23 pub fn new(revocation_singleton_launcher_id: Bytes32, verified_data: VerifiedData) -> Self {
24 Self {
25 revocation_singleton_launcher_id,
26 verified_data,
27 }
28 }
29}
30
31impl Layer for VerificationLayer {
32 type Solution = VerificationLayerSolution;
33
34 fn parse_puzzle(allocator: &Allocator, puzzle: Puzzle) -> Result<Option<Self>, DriverError> {
35 let Some(puzzle_2nd_curry) = puzzle.as_curried() else {
36 return Ok(None);
37 };
38
39 let puzzle_2nd_curry =
40 CurriedProgram::<NodePtr, NodePtr>::from_clvm(allocator, puzzle_2nd_curry.curried_ptr)?;
41 let puzzle_1st_curry = Puzzle::parse(allocator, puzzle_2nd_curry.program);
42 let Some(puzzle_1st_curry) = puzzle_1st_curry.as_curried() else {
43 return Ok(None);
44 };
45
46 if puzzle_1st_curry.mod_hash != VERIFICATION_LAYER_PUZZLE_HASH {
47 return Ok(None);
48 }
49
50 let args_2nd_curry = VerificationLayer2ndCurryArgs::<VerifiedData>::from_clvm(
51 allocator,
52 puzzle_2nd_curry.args,
53 )?;
54 let args_1st_curry =
55 VerificationLayer1stCurryArgs::from_clvm(allocator, puzzle_1st_curry.args)?;
56
57 if args_1st_curry
58 .revocation_singleton_struct
59 .launcher_puzzle_hash
60 != SINGLETON_LAUNCHER_HASH.into()
61 || args_1st_curry.revocation_singleton_struct.mod_hash
62 != SINGLETON_TOP_LAYER_V1_1_HASH.into()
63 {
64 return Err(DriverError::NonStandardLayer);
65 }
66
67 if args_2nd_curry.self_hash
68 != VerificationLayer1stCurryArgs::curry_tree_hash(
69 args_1st_curry.revocation_singleton_struct.launcher_id,
70 )
71 .into()
72 {
73 return Err(DriverError::NonStandardLayer);
74 }
75
76 Ok(Some(Self {
77 revocation_singleton_launcher_id: args_1st_curry
78 .revocation_singleton_struct
79 .launcher_id,
80 verified_data: args_2nd_curry.verified_data,
81 }))
82 }
83
84 fn parse_solution(
85 allocator: &Allocator,
86 solution: NodePtr,
87 ) -> Result<Self::Solution, DriverError> {
88 VerificationLayerSolution::from_clvm(allocator, solution).map_err(DriverError::FromClvm)
89 }
90
91 fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
92 let puzzle_1st_curry = ctx.curry(VerificationLayer1stCurryArgs {
93 revocation_singleton_struct: SingletonStruct::new(
94 self.revocation_singleton_launcher_id,
95 ),
96 })?;
97 let self_hash =
98 VerificationLayer1stCurryArgs::curry_tree_hash(self.revocation_singleton_launcher_id)
99 .into();
100
101 CurriedProgram {
102 program: puzzle_1st_curry,
103 args: VerificationLayer2ndCurryArgs {
104 self_hash,
105 verified_data: self.verified_data.clone(),
106 },
107 }
108 .to_clvm(ctx)
109 .map_err(DriverError::ToClvm)
110 }
111
112 fn construct_solution(
113 &self,
114 ctx: &mut SpendContext,
115 solution: Self::Solution,
116 ) -> Result<NodePtr, DriverError> {
117 ctx.alloc(&solution)
118 }
119}
120
121#[derive(ToClvm, FromClvm, Debug, Clone, PartialEq, Eq)]
122#[clvm(list)]
123pub struct VerifiedData {
124 pub version: u32,
125 pub asset_id: Bytes32,
126 pub data_hash: Bytes32,
127 #[clvm(rest)]
128 pub comment: String,
129}
130
131impl VerifiedData {
132 pub fn data_hash_from_cat_nft_metadata(metadata: &CatNftMetadata) -> Bytes32 {
133 clvm_list!(
134 metadata.ticker.clone(),
135 metadata.name.clone(),
136 metadata.description.clone(),
137 metadata.image_hash,
138 metadata.metadata_hash,
139 metadata.license_hash,
140 )
141 .tree_hash()
142 .into()
143 }
144
145 pub fn from_cat_nft_metadata(
146 asset_id: Bytes32,
147 metadata: &CatNftMetadata,
148 comment: String,
149 ) -> Self {
150 Self {
151 version: 1,
152 asset_id,
153 data_hash: Self::data_hash_from_cat_nft_metadata(metadata),
154 comment,
155 }
156 }
157
158 pub fn get_hint(&self) -> Bytes32 {
159 self.data_hash
160 }
161}