chia_sdk_driver/primitives/action_layer/
verification.rs1use chia_protocol::{Bytes32, Coin, CoinSpend};
2use chia_puzzle_types::singleton::{SingletonArgs, SingletonSolution};
3use chia_puzzle_types::{EveProof, LineageProof, Proof};
4use chia_puzzles::SINGLETON_LAUNCHER_HASH;
5use chia_sdk_types::puzzles::{VerificationLayer2ndCurryArgs, VerificationLayerSolution};
6use clvm_traits::{FromClvm, ToClvm};
7use clvm_utils::{ToTreeHash, TreeHash};
8use clvmr::{Allocator, NodePtr};
9
10use crate::{
11 DriverError, Layer, Puzzle, SingletonLayer, SpendContext, VerificationLayer, VerifiedData,
12};
13
14use super::VerificationInfo;
15
16type VerificationLayers = SingletonLayer<VerificationLayer>;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19#[must_use]
20pub struct Verification {
21 pub coin: Coin,
22 pub proof: Proof,
23
24 pub info: VerificationInfo,
25}
26
27impl Verification {
28 pub fn new(coin: Coin, proof: Proof, info: VerificationInfo) -> Self {
29 Self { coin, proof, info }
30 }
31
32 pub fn after_mint(
33 launcher_parent: Bytes32,
34 revocation_singleton_launcher_id: Bytes32,
35 verified_data: VerifiedData,
36 ) -> Self {
37 let launcher_coin = Coin::new(launcher_parent, SINGLETON_LAUNCHER_HASH.into(), 0);
38 let verification_launcher_id = launcher_coin.coin_id();
39
40 let info = VerificationInfo {
41 launcher_id: verification_launcher_id,
42 revocation_singleton_launcher_id,
43 verified_data,
44 };
45
46 Self {
47 coin: Coin::new(verification_launcher_id, Self::puzzle_hash(&info).into(), 1),
48 proof: Proof::Eve(EveProof {
49 parent_parent_coin_info: launcher_parent,
50 parent_amount: 0,
51 }),
52 info,
53 }
54 }
55
56 pub fn inner_puzzle_hash<T>(
57 revocation_singleton_launcher_id: Bytes32,
58 verified_data: &T,
59 ) -> TreeHash
60 where
61 T: ToTreeHash,
62 {
63 VerificationLayer2ndCurryArgs::curry_tree_hash(
64 revocation_singleton_launcher_id,
65 verified_data,
66 )
67 }
68
69 pub fn puzzle_hash(info: &VerificationInfo) -> TreeHash {
70 SingletonArgs::curry_tree_hash(
71 info.launcher_id,
72 Self::inner_puzzle_hash(
73 info.revocation_singleton_launcher_id,
74 &info.verified_data.tree_hash(),
75 ),
76 )
77 }
78
79 pub fn into_layers(self) -> VerificationLayers {
80 SingletonLayer::new(
81 self.info.launcher_id,
82 VerificationLayer::new(
83 self.info.revocation_singleton_launcher_id,
84 self.info.verified_data,
85 ),
86 )
87 }
88
89 pub fn into_layers_with_clone(&self) -> VerificationLayers {
90 SingletonLayer::new(
91 self.info.launcher_id,
92 VerificationLayer::new(
93 self.info.revocation_singleton_launcher_id,
94 self.info.verified_data.clone(),
95 ),
96 )
97 }
98
99 pub fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
100 let layers = self.into_layers_with_clone();
101
102 layers.construct_puzzle(ctx)
103 }
104
105 pub fn spend(
106 self,
107 ctx: &mut SpendContext,
108 revocation_singleton_inner_puzzle_hash: Option<Bytes32>,
109 ) -> Result<(), DriverError> {
110 let sol = SingletonSolution {
111 lineage_proof: self.proof,
112 amount: self.coin.amount,
113 inner_solution: VerificationLayerSolution {
114 revocation_singleton_inner_puzzle_hash,
115 },
116 };
117 let my_coin = self.coin;
118
119 let layers = self.into_layers();
120
121 let puzzle_reveal = layers.construct_puzzle(ctx)?;
122 let solution = layers.construct_solution(ctx, sol)?;
123
124 let puzzle_reveal = ctx.serialize(&puzzle_reveal)?;
125 let solution = ctx.serialize(&solution)?;
126
127 ctx.insert(CoinSpend::new(my_coin, puzzle_reveal, solution));
128
129 Ok(())
130 }
131}
132
133impl Verification {
134 pub fn from_parent_spend(
135 allocator: &mut Allocator,
136 parent_coin: Coin,
137 parent_puzzle: Puzzle,
138 ) -> Result<Option<Self>, DriverError> {
139 let Some(parent_layers) = VerificationLayers::parse_puzzle(allocator, parent_puzzle)?
140 else {
141 return Ok(None);
142 };
143
144 let parent_inner_puzzle_hash = Verification::inner_puzzle_hash(
145 parent_layers.inner_puzzle.revocation_singleton_launcher_id,
146 &parent_layers.inner_puzzle.verified_data.tree_hash(),
147 )
148 .into();
149
150 Ok(Some(Self {
151 coin: Coin::new(parent_coin.coin_id(), parent_coin.puzzle_hash, 1),
152 proof: Proof::Lineage(LineageProof {
153 parent_parent_coin_info: parent_coin.parent_coin_info,
154 parent_inner_puzzle_hash,
155 parent_amount: parent_coin.amount,
156 }),
157 info: VerificationInfo::new(
158 parent_layers.launcher_id,
159 parent_layers.inner_puzzle.revocation_singleton_launcher_id,
160 parent_layers.inner_puzzle.verified_data,
161 ),
162 }))
163 }
164}
165
166#[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)]
167#[clvm(list)]
168pub struct VerificationLauncherKVList {
169 pub revocation_singleton_launcher_id: Bytes32,
170 pub verified_data: VerifiedData,
171}
172
173#[cfg(test)]
174mod tests {
175 use anyhow::Ok;
176 use chia_protocol::Bytes;
177 use chia_puzzle_types::Memos;
178 use chia_puzzles::SINGLETON_LAUNCHER_HASH;
179 use chia_sdk_test::Simulator;
180 use chia_sdk_types::Conditions;
181
182 use crate::{Launcher, SingletonInfo, StandardLayer, VerificationAsserter, VerifiedData};
183
184 use super::*;
185
186 #[test]
187 fn test_verifications_and_asserter() -> anyhow::Result<()> {
188 let mut sim = Simulator::new();
189 let ctx = &mut SpendContext::new();
190 let bls = sim.bls(1);
191 let p2 = StandardLayer::new(bls.pk);
192
193 let did_launcher = Launcher::new(bls.coin.coin_id(), 1);
194 let (create_did, did) = did_launcher.create_simple_did(ctx, &p2)?;
195 p2.spend(ctx, bls.coin, create_did)?;
196
197 let verifier_proof = did.child_lineage_proof();
198 let did = did.update(
199 ctx,
200 &p2,
201 Conditions::new().create_coin(SINGLETON_LAUNCHER_HASH.into(), 0, Memos::None),
202 )?;
203 let verification_launcher = Launcher::new(did.coin.parent_coin_info, 0);
204 let verified_data = VerifiedData {
207 version: 1,
208 asset_id: Bytes32::new([2; 32]),
209 data_hash: Bytes32::new([3; 32]),
210 comment: "Test verification for test testing purposes only.".to_string(),
211 };
212 let verification = Verification::after_mint(
213 verification_launcher.coin().parent_coin_info,
214 did.info.launcher_id,
215 verified_data.clone(),
216 );
217
218 let (_conds, new_coin) = verification_launcher.with_singleton_amount(1).spend(
219 ctx,
220 Verification::inner_puzzle_hash(
221 verification.info.revocation_singleton_launcher_id,
222 &verification.info.verified_data,
223 )
224 .into(),
225 (),
226 )?;
227
228 assert_eq!(new_coin, verification.coin);
229
230 verification.clone().spend(ctx, None)?;
232
233 let parent_puzzle = verification.construct_puzzle(ctx)?;
234 let parent_puzzle = Puzzle::parse(ctx, parent_puzzle);
235 let verification =
236 Verification::from_parent_spend(ctx, verification.coin, parent_puzzle)?.unwrap();
237
238 let verification_asserter = VerificationAsserter::from(
240 did.info.launcher_id,
241 verified_data.version,
242 verified_data.asset_id.tree_hash(),
243 verified_data.data_hash.tree_hash(),
244 );
245
246 let payment_coin = sim.new_coin(verification_asserter.tree_hash().into(), 1337);
247 verification_asserter.spend(ctx, payment_coin, verifier_proof, 0, verified_data.comment)?;
248
249 let revocation_singleton_inner_ph = did.info.inner_puzzle_hash().into();
251
252 let msg_data = ctx.alloc(&verification.coin.puzzle_hash)?;
253 let _ = did.update(
254 ctx,
255 &p2,
256 Conditions::new().send_message(18, Bytes::default(), vec![msg_data]),
257 )?;
258
259 verification.spend(ctx, Some(revocation_singleton_inner_ph))?;
260
261 sim.spend_coins(ctx.take(), &[bls.sk])?;
262
263 Ok(())
264 }
265}