chia_sdk_driver/primitives/action_layer/
reserve.rs

1use 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        // prepend CREATE_COIN, just like the reserve finalizer does
140        // (list CREATE_COIN RESERVE_INNER_PUZZLE_HASH (f New_State) (list RESERVE_INNER_PUZZLE_HASH))
141        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}