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}