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}