chia_sdk_driver/primitives/action_layer/
reserve.rs1use chia_protocol::{Bytes32, Coin};
2use chia_puzzle_types::{LineageProof, cat::CatArgs, singleton::SingletonSolution};
3use chia_sdk_types::{
4 conditions::CreateCoin,
5 puzzles::{P2DelegatedBySingletonLayerArgs, P2DelegatedBySingletonLayerSolution},
6 run_puzzle,
7};
8use clvm_traits::{FromClvm, ToClvm, clvm_list, clvm_quote, match_tuple};
9use clvm_utils::TreeHash;
10use clvmr::{Allocator, NodePtr};
11
12use crate::{
13 ActionLayer, Cat, CatInfo, CatSpend, DriverError, Layer, P2DelegatedBySingletonLayer, Spend,
14 SpendContext,
15};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18#[must_use]
19pub struct Reserve {
20 pub coin: Coin,
21 pub asset_id: Bytes32,
22 pub proof: LineageProof,
23 pub inner_puzzle_hash: Bytes32,
24
25 pub controller_singleton_struct_hash: Bytes32,
26 pub nonce: u64,
27}
28
29pub trait Reserveful {
30 fn reserve_amount(&self, index: u64) -> u64;
31}
32
33impl Reserve {
34 pub fn new(
35 parent_coin_id: Bytes32,
36 proof: LineageProof,
37 asset_id: Bytes32,
38 controller_singleton_struct_hash: Bytes32,
39 nonce: u64,
40 amount: u64,
41 ) -> Self {
42 let inner_puzzle_hash = P2DelegatedBySingletonLayerArgs::curry_tree_hash(
43 controller_singleton_struct_hash,
44 nonce,
45 );
46
47 Self {
48 coin: Coin::new(
49 parent_coin_id,
50 CatArgs::curry_tree_hash(asset_id, inner_puzzle_hash).into(),
51 amount,
52 ),
53 proof,
54 asset_id,
55 inner_puzzle_hash: inner_puzzle_hash.into(),
56 controller_singleton_struct_hash,
57 nonce,
58 }
59 }
60
61 pub fn puzzle_hash(
62 asset_id: Bytes32,
63 controller_singleton_struct_hash: Bytes32,
64 nonce: u64,
65 ) -> TreeHash {
66 CatArgs::curry_tree_hash(
67 asset_id,
68 P2DelegatedBySingletonLayerArgs::curry_tree_hash(
69 controller_singleton_struct_hash,
70 nonce,
71 ),
72 )
73 }
74
75 pub fn construct_inner_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
76 let layer =
77 P2DelegatedBySingletonLayer::new(self.controller_singleton_struct_hash, self.nonce);
78
79 layer.construct_puzzle(ctx)
80 }
81
82 pub fn to_cat(&self) -> Cat {
83 Cat::new(
84 self.coin,
85 Some(self.proof),
86 CatInfo::new(self.asset_id, None, self.inner_puzzle_hash),
87 )
88 }
89
90 pub fn inner_spend(
91 &self,
92 ctx: &mut SpendContext,
93 controller_singleton_inner_puzzle_hash: Bytes32,
94 delegated_puzzle: NodePtr,
95 delegated_solution: NodePtr,
96 ) -> Result<Spend, DriverError> {
97 P2DelegatedBySingletonLayer::new(self.controller_singleton_struct_hash, self.nonce)
98 .construct_spend(
99 ctx,
100 P2DelegatedBySingletonLayerSolution {
101 singleton_inner_puzzle_hash: controller_singleton_inner_puzzle_hash,
102 delegated_puzzle,
103 delegated_solution,
104 },
105 )
106 }
107
108 pub fn delegated_puzzle_for_finalizer_controller<S>(
109 &self,
110 ctx: &mut SpendContext,
111 controlelr_initial_state: S,
112 controller_solution: NodePtr,
113 ) -> Result<NodePtr, DriverError>
114 where
115 S: ToClvm<Allocator> + FromClvm<Allocator> + Clone + Reserveful,
116 {
117 let controller_solution = ctx.extract::<SingletonSolution<NodePtr>>(controller_solution)?;
118 let inner_solution =
119 ActionLayer::<S, NodePtr>::parse_solution(ctx, controller_solution.inner_solution)?;
120
121 let mut state = (NodePtr::NIL, controlelr_initial_state);
122 let mut reserve_conditions = Vec::new();
123 for raw_action in inner_solution.action_spends {
124 let actual_solution = ctx.alloc(&clvm_list!(state, raw_action.solution))?;
125
126 let output = run_puzzle(ctx, raw_action.puzzle, actual_solution)?;
127
128 let (new_state, conditions) =
129 ctx.extract::<match_tuple!((NodePtr, S), Vec<(i64, NodePtr)>)>(output)?;
130 state = new_state;
131
132 for (opcode, cond) in conditions.iter().rev() {
133 if *opcode == -42 {
134 reserve_conditions.push(*cond);
135 }
136 }
137 }
138
139 let new_reserve_amount = state.1.reserve_amount(0);
142 let cc = CreateCoin::new(
143 self.inner_puzzle_hash,
144 new_reserve_amount,
145 ctx.hint(self.inner_puzzle_hash)?,
146 );
147 reserve_conditions.insert(0, ctx.alloc(&cc)?);
148
149 let delegated_puzzle = ctx.alloc(&clvm_quote!(reserve_conditions))?;
150
151 Ok(delegated_puzzle)
152 }
153
154 pub fn cat_spend_for_reserve_finalizer_controller<S>(
155 &self,
156 ctx: &mut SpendContext,
157 controlelr_initial_state: S,
158 controller_singleton_inner_puzzle_hash: Bytes32,
159 controller_solution: NodePtr,
160 ) -> Result<CatSpend, DriverError>
161 where
162 S: ToClvm<Allocator> + FromClvm<Allocator> + Clone + Reserveful,
163 {
164 let delegated_puzzle = self.delegated_puzzle_for_finalizer_controller(
165 ctx,
166 controlelr_initial_state,
167 controller_solution,
168 )?;
169
170 Ok(CatSpend::new(
171 self.to_cat(),
172 self.inner_spend(
173 ctx,
174 controller_singleton_inner_puzzle_hash,
175 delegated_puzzle,
176 NodePtr::NIL,
177 )?,
178 ))
179 }
180
181 pub fn child(&self, child_amount: u64) -> Self {
182 Self::new(
183 self.coin.coin_id(),
184 LineageProof {
185 parent_parent_coin_info: self.coin.parent_coin_info,
186 parent_inner_puzzle_hash: self.inner_puzzle_hash,
187 parent_amount: self.coin.amount,
188 },
189 self.asset_id,
190 self.controller_singleton_struct_hash,
191 self.nonce,
192 child_amount,
193 )
194 }
195}