chia_sdk_driver/primitives/mips/
inner_puzzle_spend.rs1use 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}