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}