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