chia_sdk_driver/primitives/action_layer/
precommit_coin.rs

1use chia_protocol::{Bytes32, Coin};
2use chia_puzzle_types::{
3    cat::{CatArgs, CatSolution},
4    CoinProof, LineageProof,
5};
6use chia_sdk_types::puzzles::{PrecommitLayerSolution, PrecommitSpendMode};
7use clvm_traits::ToClvm;
8use clvm_utils::TreeHash;
9use clvmr::{Allocator, NodePtr};
10
11use crate::{CatLayer, DriverError, Layer, PrecommitLayer, Spend, SpendContext};
12
13#[derive(Debug, Clone)]
14#[must_use]
15pub struct PrecommitCoin<V> {
16    pub coin: Coin,
17    pub asset_id: Bytes32,
18    pub proof: LineageProof,
19    pub inner_puzzle_hash: Bytes32,
20
21    pub controller_singleton_struct_hash: Bytes32,
22    pub relative_block_height: u32,
23    pub payout_puzzle_hash: Bytes32,
24    pub refund_puzzle_hash: Bytes32,
25    pub value: V,
26}
27
28impl<V> PrecommitCoin<V> {
29    #[allow(clippy::too_many_arguments)]
30    pub fn new(
31        ctx: &mut SpendContext,
32        parent_coin_id: Bytes32,
33        proof: LineageProof,
34        asset_id: Bytes32,
35        controller_singleton_struct_hash: Bytes32,
36        relative_block_height: u32,
37        payout_puzzle_hash: Bytes32,
38        refund_puzzle_hash: Bytes32,
39        value: V,
40        precommit_amount: u64,
41    ) -> Result<Self, DriverError>
42    where
43        V: ToClvm<Allocator> + Clone,
44    {
45        let value_ptr = ctx.alloc(&value)?;
46        let value_hash = ctx.tree_hash(value_ptr);
47
48        let inner_puzzle_hash = PrecommitLayer::<V>::puzzle_hash(
49            controller_singleton_struct_hash,
50            relative_block_height,
51            payout_puzzle_hash,
52            refund_puzzle_hash,
53            value_hash,
54        );
55
56        Ok(Self {
57            coin: Coin::new(
58                parent_coin_id,
59                CatArgs::curry_tree_hash(asset_id, inner_puzzle_hash).into(),
60                precommit_amount,
61            ),
62            proof,
63            asset_id,
64            inner_puzzle_hash: inner_puzzle_hash.into(),
65            controller_singleton_struct_hash,
66            relative_block_height,
67            payout_puzzle_hash,
68            refund_puzzle_hash,
69            value,
70        })
71    }
72
73    pub fn puzzle_hash(
74        asset_id: Bytes32,
75        controller_singleton_struct_hash: Bytes32,
76        relative_block_height: u32,
77        payout_puzzle_hash: Bytes32,
78        refund_puzzle_hash: Bytes32,
79        value_hash: TreeHash,
80    ) -> TreeHash {
81        CatArgs::curry_tree_hash(
82            asset_id,
83            PrecommitLayer::<V>::puzzle_hash(
84                controller_singleton_struct_hash,
85                relative_block_height,
86                payout_puzzle_hash,
87                refund_puzzle_hash,
88                value_hash,
89            ),
90        )
91    }
92
93    pub fn inner_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError>
94    where
95        V: Clone + ToClvm<Allocator>,
96    {
97        PrecommitLayer::<V>::new(
98            self.controller_singleton_struct_hash,
99            self.relative_block_height,
100            self.payout_puzzle_hash,
101            self.refund_puzzle_hash,
102            self.value.clone(),
103        )
104        .construct_puzzle(ctx)
105    }
106
107    pub fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError>
108    where
109        V: Clone + ToClvm<Allocator>,
110    {
111        let inner_puzzle = self.inner_puzzle(ctx)?;
112
113        CatLayer::<NodePtr>::new(self.asset_id, inner_puzzle).construct_puzzle(ctx)
114    }
115
116    pub fn construct_solution(
117        &self,
118        ctx: &mut SpendContext,
119        mode: PrecommitSpendMode,
120        singleton_inner_puzzle_hash: Bytes32,
121    ) -> Result<NodePtr, DriverError>
122    where
123        V: ToClvm<Allocator> + Clone,
124    {
125        let layers = CatLayer::<NodePtr>::new(self.asset_id, self.inner_puzzle(ctx)?);
126
127        let inner_puzzle_solution = ctx.alloc(&PrecommitLayerSolution {
128            mode,
129            my_amount: self.coin.amount,
130            singleton_inner_puzzle_hash,
131        })?;
132
133        layers.construct_solution(
134            ctx,
135            CatSolution {
136                inner_puzzle_solution,
137                lineage_proof: Some(self.proof),
138                prev_coin_id: self.coin.coin_id(),
139                this_coin_info: self.coin,
140                next_coin_proof: CoinProof {
141                    parent_coin_info: self.coin.parent_coin_info,
142                    inner_puzzle_hash: self.inner_puzzle_hash,
143                    amount: self.coin.amount,
144                },
145                prev_subtotal: 0,
146                extra_delta: 0,
147            },
148        )
149    }
150
151    pub fn spend(
152        &self,
153        ctx: &mut SpendContext,
154        mode: PrecommitSpendMode,
155        spender_inner_puzzle_hash: Bytes32,
156    ) -> Result<(), DriverError>
157    where
158        V: ToClvm<Allocator> + Clone,
159    {
160        let puzzle = self.construct_puzzle(ctx)?;
161        let solution = self.construct_solution(ctx, mode, spender_inner_puzzle_hash)?;
162
163        ctx.spend(self.coin, Spend::new(puzzle, solution))
164    }
165}