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