chia_sdk_driver/primitives/mips/
inner_puzzle_spend.rs

1use chia_sdk_types::{
2    puzzles::{
3        AddDelegatedPuzzleWrapper, AddDelegatedPuzzleWrapperSolution, DelegatedFeederArgs,
4        DelegatedFeederSolution, EnforceDelegatedPuzzleWrappers,
5        EnforceDelegatedPuzzleWrappersSolution, IndexWrapperArgs, RestrictionsArgs,
6        RestrictionsSolution,
7    },
8    Mod,
9};
10use clvm_utils::TreeHash;
11
12use crate::{DriverError, Spend, SpendContext};
13
14use super::{
15    m_of_n::MofN, mips_spend::MipsSpend, restriction::Restriction, MipsSpendKind, RestrictionKind,
16};
17
18#[derive(Debug, Clone)]
19pub struct InnerPuzzleSpend {
20    pub nonce: usize,
21    pub restrictions: Vec<Restriction>,
22    pub kind: MipsSpendKind,
23}
24
25impl InnerPuzzleSpend {
26    pub fn new(nonce: usize, restrictions: Vec<Restriction>, spend: Spend) -> Self {
27        Self {
28            nonce,
29            restrictions,
30            kind: MipsSpendKind::Member(spend),
31        }
32    }
33
34    pub fn m_of_n(
35        nonce: usize,
36        restrictions: Vec<Restriction>,
37        required: usize,
38        items: Vec<TreeHash>,
39    ) -> Self {
40        Self {
41            nonce,
42            restrictions,
43            kind: MipsSpendKind::MofN(MofN::new(required, items)),
44        }
45    }
46
47    pub fn spend(
48        &self,
49        ctx: &mut SpendContext,
50        spend: &MipsSpend,
51        delegated_puzzle_wrappers: &mut Vec<TreeHash>,
52        delegated_spend: bool,
53    ) -> Result<Spend, DriverError> {
54        let mut result = self.kind.spend(ctx, spend, delegated_puzzle_wrappers)?;
55
56        if !self.restrictions.is_empty() {
57            let mut member_validators = Vec::new();
58            let mut delegated_puzzle_validators = Vec::new();
59            let mut local_delegated_puzzle_wrappers = Vec::new();
60
61            let mut member_validator_solutions = Vec::new();
62            let mut delegated_puzzle_validator_solutions = Vec::new();
63
64            for restriction in &self.restrictions {
65                match restriction.kind {
66                    RestrictionKind::MemberCondition => {
67                        let restriction_spend = spend
68                            .restrictions
69                            .get(&restriction.puzzle_hash)
70                            .ok_or(DriverError::MissingSubpathSpend)?;
71
72                        member_validators.push(restriction_spend.puzzle);
73                        member_validator_solutions.push(restriction_spend.solution);
74                    }
75                    RestrictionKind::DelegatedPuzzleHash => {
76                        let restriction_spend = spend
77                            .restrictions
78                            .get(&restriction.puzzle_hash)
79                            .ok_or(DriverError::MissingSubpathSpend)?;
80
81                        delegated_puzzle_validators.push(restriction_spend.puzzle);
82                        delegated_puzzle_validator_solutions.push(restriction_spend.solution);
83                    }
84                    RestrictionKind::DelegatedPuzzleWrapper => {
85                        local_delegated_puzzle_wrappers.push(restriction.puzzle_hash);
86                    }
87                }
88            }
89
90            for (i, &wrapper) in local_delegated_puzzle_wrappers.iter().enumerate() {
91                if i >= delegated_puzzle_wrappers.len() {
92                    delegated_puzzle_wrappers.push(wrapper);
93                } else if delegated_puzzle_wrappers[i] != wrapper {
94                    return Err(DriverError::DelegatedPuzzleWrapperConflict);
95                }
96            }
97
98            if !local_delegated_puzzle_wrappers.is_empty() {
99                delegated_puzzle_validators.push(ctx.curry(
100                    EnforceDelegatedPuzzleWrappers::new(&local_delegated_puzzle_wrappers),
101                )?);
102
103                delegated_puzzle_validator_solutions.push(ctx.alloc(
104                    &EnforceDelegatedPuzzleWrappersSolution::new(
105                        ctx.tree_hash(spend.delegated.puzzle).into(),
106                    ),
107                )?);
108            }
109
110            result.puzzle = ctx.curry(RestrictionsArgs::new(
111                member_validators,
112                delegated_puzzle_validators,
113                result.puzzle,
114            ))?;
115
116            result.solution = ctx.alloc(&RestrictionsSolution::new(
117                member_validator_solutions,
118                delegated_puzzle_validator_solutions,
119                result.solution,
120            ))?;
121        }
122
123        if delegated_spend {
124            result.puzzle = ctx.curry(DelegatedFeederArgs::new(result.puzzle))?;
125
126            let delegated_puzzle_wrappers = delegated_puzzle_wrappers.clone();
127
128            let mut delegated_spend = spend.delegated;
129
130            for wrapper in delegated_puzzle_wrappers.into_iter().rev() {
131                let spend = spend
132                    .restrictions
133                    .get(&wrapper)
134                    .ok_or(DriverError::MissingSubpathSpend)?;
135
136                let puzzle = ctx.curry(AddDelegatedPuzzleWrapper::new(
137                    spend.puzzle,
138                    delegated_spend.puzzle,
139                ))?;
140                let solution = ctx.alloc(&AddDelegatedPuzzleWrapperSolution::new(
141                    spend.solution,
142                    delegated_spend.solution,
143                ))?;
144
145                delegated_spend = Spend::new(puzzle, solution);
146            }
147
148            result.solution = ctx.alloc(&DelegatedFeederSolution::new(
149                delegated_spend.puzzle,
150                delegated_spend.solution,
151                result.solution,
152            ))?;
153        }
154
155        Ok(Spend::new(
156            ctx.curry(IndexWrapperArgs::new(self.nonce, result.puzzle))?,
157            result.solution,
158        ))
159    }
160}
161
162pub fn member_puzzle_hash(
163    nonce: usize,
164    restrictions: Vec<Restriction>,
165    inner_puzzle_hash: TreeHash,
166    top_level: bool,
167) -> TreeHash {
168    let mut puzzle_hash = inner_puzzle_hash;
169
170    if !restrictions.is_empty() {
171        let mut member_validators = Vec::new();
172        let mut delegated_puzzle_validators = Vec::new();
173        let mut delegated_puzzle_wrappers = Vec::new();
174
175        for restriction in restrictions {
176            match restriction.kind {
177                RestrictionKind::MemberCondition => {
178                    member_validators.push(restriction.puzzle_hash);
179                }
180                RestrictionKind::DelegatedPuzzleHash => {
181                    delegated_puzzle_validators.push(restriction.puzzle_hash);
182                }
183                RestrictionKind::DelegatedPuzzleWrapper => {
184                    delegated_puzzle_wrappers.push(restriction.puzzle_hash);
185                }
186            }
187        }
188
189        if !delegated_puzzle_wrappers.is_empty() {
190            delegated_puzzle_validators.push(
191                EnforceDelegatedPuzzleWrappers::new(&delegated_puzzle_wrappers).curry_tree_hash(),
192            );
193        }
194
195        puzzle_hash =
196            RestrictionsArgs::new(member_validators, delegated_puzzle_validators, puzzle_hash)
197                .curry_tree_hash();
198    }
199
200    if top_level {
201        puzzle_hash = DelegatedFeederArgs::new(puzzle_hash).curry_tree_hash();
202    }
203
204    IndexWrapperArgs::new(nonce, puzzle_hash).curry_tree_hash()
205}