chia_sdk_driver/primitives/mips/
memo.rs

1mod parsed_member;
2mod parsed_restriction;
3mod parsed_wrapper;
4
5use chia_consensus::opcodes::{
6    CREATE_COIN_ANNOUNCEMENT, CREATE_PUZZLE_ANNOUNCEMENT, RECEIVE_MESSAGE, SEND_MESSAGE,
7};
8pub use parsed_member::*;
9pub use parsed_restriction::*;
10pub use parsed_wrapper::*;
11
12use chia_bls::PublicKey;
13use chia_protocol::Bytes32;
14use chia_sdk_types::{
15    puzzles::{
16        BlsMember, BlsTaprootMember, EnforceDelegatedPuzzleWrappers, FixedPuzzleMember,
17        Force1of2RestrictedVariable, K1Member, K1MemberPuzzleAssert, PasskeyMember,
18        PasskeyMemberPuzzleAssert, PreventConditionOpcode, R1Member, R1MemberPuzzleAssert,
19        SingletonMember, Timelock, FORCE_ASSERT_COIN_ANNOUNCEMENT_PUZZLE_HASH,
20        FORCE_COIN_MESSAGE_PUZZLE_HASH, PREVENT_MULTIPLE_CREATE_COINS_PUZZLE_HASH,
21    },
22    Mod,
23};
24use chia_secp::{K1PublicKey, R1PublicKey};
25use clvm_traits::{apply_constants, FromClvm, ToClvm};
26use clvm_utils::TreeHash;
27use clvmr::{Allocator, NodePtr};
28
29use crate::DriverError;
30
31#[derive(ToClvm, FromClvm)]
32#[apply_constants]
33#[derive(Debug, Clone, PartialEq, Eq)]
34#[clvm(list)]
35pub struct MipsMemo<T = NodePtr> {
36    #[clvm(constant = "CHIP-0043".to_string())]
37    pub namespace: String,
38    pub inner_puzzle: InnerPuzzleMemo<T>,
39}
40
41impl MipsMemo<NodePtr> {
42    pub fn new(inner_puzzle: InnerPuzzleMemo) -> Self {
43        Self { inner_puzzle }
44    }
45}
46
47#[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)]
48#[clvm(list)]
49pub struct InnerPuzzleMemo<T = NodePtr> {
50    pub nonce: usize,
51    pub restrictions: Vec<RestrictionMemo<T>>,
52    #[clvm(rest)]
53    pub kind: MemoKind<T>,
54}
55
56impl InnerPuzzleMemo<NodePtr> {
57    pub fn new(nonce: usize, restrictions: Vec<RestrictionMemo>, kind: MemoKind) -> Self {
58        Self {
59            nonce,
60            restrictions,
61            kind,
62        }
63    }
64}
65
66#[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)]
67#[clvm(list)]
68pub struct RestrictionMemo<T = NodePtr> {
69    pub member_condition_validator: bool,
70    pub puzzle_hash: Bytes32,
71    pub memo: T,
72}
73
74impl RestrictionMemo<NodePtr> {
75    pub fn new(member_condition_validator: bool, puzzle_hash: Bytes32, memo: NodePtr) -> Self {
76        Self {
77            member_condition_validator,
78            puzzle_hash,
79            memo,
80        }
81    }
82
83    pub fn force_1_of_2_restricted_variable(
84        allocator: &mut Allocator,
85        left_side_subtree_hash: Bytes32,
86        nonce: usize,
87        member_validator_list_hash: Bytes32,
88        delegated_puzzle_validator_list_hash: Bytes32,
89    ) -> Result<Self, DriverError> {
90        Ok(Self::new(
91            false,
92            Force1of2RestrictedVariable::new(
93                left_side_subtree_hash,
94                nonce,
95                member_validator_list_hash,
96                delegated_puzzle_validator_list_hash,
97            )
98            .curry_tree_hash()
99            .into(),
100            Force1of2RestrictedVariableMemo::new(
101                left_side_subtree_hash,
102                nonce,
103                member_validator_list_hash,
104                delegated_puzzle_validator_list_hash,
105            )
106            .to_clvm(allocator)?,
107        ))
108    }
109
110    pub fn enforce_delegated_puzzle_wrappers(
111        allocator: &mut Allocator,
112        wrapper_memos: &[WrapperMemo],
113    ) -> Result<Self, DriverError> {
114        let wrapper_stack: Vec<TreeHash> = wrapper_memos
115            .iter()
116            .map(|item| TreeHash::from(item.puzzle_hash))
117            .collect();
118
119        let memos = wrapper_memos
120            .iter()
121            .map(|item| item.memo)
122            .collect::<Vec<NodePtr>>();
123
124        Ok(Self::new(
125            false,
126            EnforceDelegatedPuzzleWrappers::new(&wrapper_stack)
127                .curry_tree_hash()
128                .into(),
129            memos.to_clvm(allocator)?,
130        ))
131    }
132
133    pub fn timelock(
134        allocator: &mut Allocator,
135        seconds: u64,
136        reveal: bool,
137    ) -> Result<Self, DriverError> {
138        Ok(Self::new(
139            true,
140            Timelock::new(seconds).curry_tree_hash().into(),
141            if reveal {
142                seconds.to_clvm(allocator)?
143            } else {
144                NodePtr::NIL
145            },
146        ))
147    }
148
149    pub fn parse(&self, allocator: &Allocator, ctx: &MipsMemoContext) -> Option<ParsedRestriction> {
150        if let Ok(items) = Vec::<WrapperMemo>::from_clvm(allocator, self.memo) {
151            let wrapper_stack: Vec<TreeHash> = items
152                .iter()
153                .map(|item| TreeHash::from(item.puzzle_hash))
154                .collect();
155
156            let restriction = EnforceDelegatedPuzzleWrappers::new(&wrapper_stack);
157
158            if restriction.curry_tree_hash() == self.puzzle_hash.into() {
159                return Some(ParsedRestriction::EnforceDelegatedPuzzleWrappers(
160                    restriction,
161                    items.iter().map(|item| item.memo).collect(),
162                ));
163            }
164        }
165
166        if let Ok(memo) = Force1of2RestrictedVariableMemo::from_clvm(allocator, self.memo) {
167            let restriction = Force1of2RestrictedVariable::new(
168                memo.left_side_subtree_hash,
169                memo.nonce,
170                memo.member_validator_list_hash,
171                memo.delegated_puzzle_validator_list_hash,
172            );
173
174            if restriction.curry_tree_hash() == self.puzzle_hash.into() {
175                return Some(ParsedRestriction::Force1of2RestrictedVariable(restriction));
176            }
177        }
178
179        if let Ok(seconds) = Option::<u64>::from_clvm(allocator, self.memo) {
180            for &seconds in seconds.iter().chain(ctx.timelocks.iter()) {
181                let restriction = Timelock::new(seconds);
182
183                if restriction.curry_tree_hash() == self.puzzle_hash.into() {
184                    return Some(ParsedRestriction::Timelock(restriction));
185                }
186            }
187        }
188
189        None
190    }
191}
192
193#[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)]
194#[clvm(list)]
195pub struct WrapperMemo<T = NodePtr> {
196    pub puzzle_hash: Bytes32,
197    pub memo: T,
198}
199
200impl WrapperMemo<NodePtr> {
201    pub fn new(puzzle_hash: Bytes32, memo: NodePtr) -> Self {
202        Self { puzzle_hash, memo }
203    }
204
205    pub fn force_assert_coin_announcement() -> Self {
206        Self {
207            puzzle_hash: FORCE_ASSERT_COIN_ANNOUNCEMENT_PUZZLE_HASH.into(),
208            memo: NodePtr::NIL,
209        }
210    }
211
212    pub fn force_coin_message() -> Self {
213        Self {
214            puzzle_hash: FORCE_COIN_MESSAGE_PUZZLE_HASH.into(),
215            memo: NodePtr::NIL,
216        }
217    }
218
219    pub fn prevent_multiple_create_coins() -> Self {
220        Self {
221            puzzle_hash: PREVENT_MULTIPLE_CREATE_COINS_PUZZLE_HASH.into(),
222            memo: NodePtr::NIL,
223        }
224    }
225
226    pub fn timelock(
227        allocator: &mut Allocator,
228        seconds: u64,
229        reveal: bool,
230    ) -> Result<Self, DriverError> {
231        Ok(Self {
232            puzzle_hash: Timelock::new(seconds).curry_tree_hash().into(),
233            memo: if reveal {
234                seconds.to_clvm(allocator)?
235            } else {
236                NodePtr::NIL
237            },
238        })
239    }
240
241    pub fn prevent_condition_opcode(
242        allocator: &mut Allocator,
243        opcode: u16,
244        reveal: bool,
245    ) -> Result<Self, DriverError> {
246        Ok(Self {
247            puzzle_hash: PreventConditionOpcode::new(opcode).curry_tree_hash().into(),
248            memo: if reveal {
249                opcode.to_clvm(allocator)?
250            } else {
251                NodePtr::NIL
252            },
253        })
254    }
255
256    pub fn parse(&self, allocator: &Allocator, ctx: &MipsMemoContext) -> Option<ParsedWrapper> {
257        if self.puzzle_hash == FORCE_ASSERT_COIN_ANNOUNCEMENT_PUZZLE_HASH.into() {
258            return Some(ParsedWrapper::ForceAssertCoinAnnouncement);
259        }
260
261        if self.puzzle_hash == FORCE_COIN_MESSAGE_PUZZLE_HASH.into() {
262            return Some(ParsedWrapper::ForceCoinMessage);
263        }
264
265        if self.puzzle_hash == PREVENT_MULTIPLE_CREATE_COINS_PUZZLE_HASH.into() {
266            return Some(ParsedWrapper::PreventMultipleCreateCoins);
267        }
268
269        if let Ok(seconds) = Option::<u64>::from_clvm(allocator, self.memo) {
270            for &seconds in seconds.iter().chain(ctx.timelocks.iter()) {
271                let wrapper = Timelock::new(seconds);
272
273                if wrapper.curry_tree_hash() == self.puzzle_hash.into() {
274                    return Some(ParsedWrapper::Timelock(wrapper));
275                }
276            }
277        }
278
279        if let Ok(opcode) = Option::<u16>::from_clvm(allocator, self.memo) {
280            for &opcode in opcode.iter().chain(ctx.opcodes.iter()) {
281                let wrapper = PreventConditionOpcode::new(opcode);
282
283                if wrapper.curry_tree_hash() == self.puzzle_hash.into() {
284                    return Some(ParsedWrapper::PreventConditionOpcode(wrapper));
285                }
286            }
287        }
288
289        None
290    }
291}
292
293#[derive(Debug, Clone, Copy, PartialEq, Eq, ToClvm, FromClvm)]
294#[clvm(list)]
295pub struct Force1of2RestrictedVariableMemo {
296    pub left_side_subtree_hash: Bytes32,
297    pub nonce: usize,
298    pub member_validator_list_hash: Bytes32,
299    pub delegated_puzzle_validator_list_hash: Bytes32,
300}
301
302impl Force1of2RestrictedVariableMemo {
303    pub fn new(
304        left_side_subtree_hash: Bytes32,
305        nonce: usize,
306        member_validator_list_hash: Bytes32,
307        delegated_puzzle_validator_list_hash: Bytes32,
308    ) -> Self {
309        Self {
310            left_side_subtree_hash,
311            nonce,
312            member_validator_list_hash,
313            delegated_puzzle_validator_list_hash,
314        }
315    }
316}
317
318#[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)]
319#[clvm(list)]
320pub enum MemoKind<T = NodePtr> {
321    Member(MemberMemo<T>),
322    MofN(MofNMemo<T>),
323}
324
325#[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)]
326#[clvm(list)]
327pub struct MemberMemo<T = NodePtr> {
328    pub puzzle_hash: Bytes32,
329    pub memo: T,
330}
331
332impl MemberMemo<NodePtr> {
333    pub fn new(puzzle_hash: Bytes32, memo: NodePtr) -> Self {
334        Self { puzzle_hash, memo }
335    }
336
337    pub fn k1(
338        allocator: &mut Allocator,
339        public_key: K1PublicKey,
340        fast_forward: bool,
341        reveal: bool,
342    ) -> Result<Self, DriverError> {
343        Ok(Self::new(
344            if fast_forward {
345                K1MemberPuzzleAssert::new(public_key).curry_tree_hash()
346            } else {
347                K1Member::new(public_key).curry_tree_hash()
348            }
349            .into(),
350            if reveal {
351                public_key.to_clvm(allocator)?
352            } else {
353                NodePtr::NIL
354            },
355        ))
356    }
357
358    pub fn r1(
359        allocator: &mut Allocator,
360        public_key: R1PublicKey,
361        fast_forward: bool,
362        reveal: bool,
363    ) -> Result<Self, DriverError> {
364        Ok(Self::new(
365            if fast_forward {
366                R1MemberPuzzleAssert::new(public_key).curry_tree_hash()
367            } else {
368                R1Member::new(public_key).curry_tree_hash()
369            }
370            .into(),
371            if reveal {
372                public_key.to_clvm(allocator)?
373            } else {
374                NodePtr::NIL
375            },
376        ))
377    }
378
379    pub fn bls(
380        allocator: &mut Allocator,
381        public_key: PublicKey,
382        taproot: bool,
383        reveal: bool,
384    ) -> Result<Self, DriverError> {
385        Ok(Self::new(
386            if taproot {
387                BlsTaprootMember::new(public_key).curry_tree_hash().into()
388            } else {
389                BlsMember::new(public_key).curry_tree_hash().into()
390            },
391            if reveal {
392                public_key.to_clvm(allocator)?
393            } else {
394                NodePtr::NIL
395            },
396        ))
397    }
398
399    pub fn passkey(
400        allocator: &mut Allocator,
401        public_key: R1PublicKey,
402        fast_forward: bool,
403        reveal: bool,
404    ) -> Result<Self, DriverError> {
405        Ok(Self::new(
406            if fast_forward {
407                PasskeyMemberPuzzleAssert::new(public_key).curry_tree_hash()
408            } else {
409                PasskeyMember::new(public_key).curry_tree_hash()
410            }
411            .into(),
412            if reveal {
413                public_key.to_clvm(allocator)?
414            } else {
415                NodePtr::NIL
416            },
417        ))
418    }
419
420    pub fn singleton(
421        allocator: &mut Allocator,
422        launcher_id: Bytes32,
423        reveal: bool,
424    ) -> Result<Self, DriverError> {
425        Ok(Self::new(
426            SingletonMember::new(launcher_id).curry_tree_hash().into(),
427            if reveal {
428                launcher_id.to_clvm(allocator)?
429            } else {
430                NodePtr::NIL
431            },
432        ))
433    }
434
435    pub fn fixed_puzzle(
436        allocator: &mut Allocator,
437        puzzle_hash: Bytes32,
438        reveal: bool,
439    ) -> Result<Self, DriverError> {
440        Ok(Self::new(
441            FixedPuzzleMember::new(puzzle_hash).curry_tree_hash().into(),
442            if reveal {
443                puzzle_hash.to_clvm(allocator)?
444            } else {
445                NodePtr::NIL
446            },
447        ))
448    }
449
450    pub fn parse(&self, allocator: &Allocator, ctx: &MipsMemoContext) -> Option<ParsedMember> {
451        for &public_key in Option::<PublicKey>::from_clvm(allocator, self.memo)
452            .ok()
453            .flatten()
454            .iter()
455            .chain(ctx.bls.iter())
456        {
457            let member = BlsMember::new(public_key);
458            if member.curry_tree_hash() == self.puzzle_hash.into() {
459                return Some(ParsedMember::Bls(member));
460            }
461
462            let member = BlsTaprootMember::new(public_key);
463            if member.curry_tree_hash() == self.puzzle_hash.into() {
464                return Some(ParsedMember::BlsTaproot(member));
465            }
466        }
467
468        for &public_key in Option::<K1PublicKey>::from_clvm(allocator, self.memo)
469            .ok()
470            .flatten()
471            .iter()
472            .chain(ctx.k1.iter())
473        {
474            let member = K1Member::new(public_key);
475            if member.curry_tree_hash() == self.puzzle_hash.into() {
476                return Some(ParsedMember::K1(member));
477            }
478
479            let member = K1MemberPuzzleAssert::new(public_key);
480            if member.curry_tree_hash() == self.puzzle_hash.into() {
481                return Some(ParsedMember::K1PuzzleAssert(member));
482            }
483        }
484
485        for &public_key in Option::<R1PublicKey>::from_clvm(allocator, self.memo)
486            .ok()
487            .flatten()
488            .iter()
489            .chain(ctx.r1.iter())
490        {
491            let member = R1Member::new(public_key);
492            if member.curry_tree_hash() == self.puzzle_hash.into() {
493                return Some(ParsedMember::R1(member));
494            }
495
496            let member = R1MemberPuzzleAssert::new(public_key);
497            if member.curry_tree_hash() == self.puzzle_hash.into() {
498                return Some(ParsedMember::R1PuzzleAssert(member));
499            }
500
501            let member = PasskeyMember::new(public_key);
502            if member.curry_tree_hash() == self.puzzle_hash.into() {
503                return Some(ParsedMember::Passkey(member));
504            }
505
506            let member = PasskeyMemberPuzzleAssert::new(public_key);
507            if member.curry_tree_hash() == self.puzzle_hash.into() {
508                return Some(ParsedMember::PasskeyPuzzleAssert(member));
509            }
510        }
511
512        for &hash in Option::<Bytes32>::from_clvm(allocator, self.memo)
513            .ok()
514            .flatten()
515            .iter()
516            .chain(ctx.hashes.iter())
517        {
518            let member = SingletonMember::new(hash);
519            if member.curry_tree_hash() == self.puzzle_hash.into() {
520                return Some(ParsedMember::Singleton(member));
521            }
522
523            let member = FixedPuzzleMember::new(hash);
524            if member.curry_tree_hash() == self.puzzle_hash.into() {
525                return Some(ParsedMember::FixedPuzzle(member));
526            }
527        }
528
529        None
530    }
531}
532
533#[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)]
534#[clvm(list)]
535pub struct MofNMemo<T = NodePtr> {
536    pub required: usize,
537    pub items: Vec<InnerPuzzleMemo<T>>,
538}
539
540impl MofNMemo<NodePtr> {
541    pub fn new(required: usize, items: Vec<InnerPuzzleMemo>) -> Self {
542        Self { required, items }
543    }
544}
545
546#[derive(Debug, Clone)]
547pub struct MipsMemoContext {
548    pub k1: Vec<K1PublicKey>,
549    pub r1: Vec<R1PublicKey>,
550    pub bls: Vec<PublicKey>,
551    pub hashes: Vec<Bytes32>,
552    pub timelocks: Vec<u64>,
553    pub opcodes: Vec<u16>,
554}
555
556impl Default for MipsMemoContext {
557    fn default() -> Self {
558        Self {
559            k1: vec![],
560            r1: vec![],
561            bls: vec![],
562            hashes: vec![],
563            timelocks: vec![],
564            opcodes: vec![
565                SEND_MESSAGE,
566                RECEIVE_MESSAGE,
567                CREATE_PUZZLE_ANNOUNCEMENT,
568                CREATE_COIN_ANNOUNCEMENT,
569            ],
570        }
571    }
572}