1use crate::error::SignerError;
23
24#[allow(dead_code)]
29mod op {
30 pub const OP_0: u8 = 0x00;
31 pub const OP_IF: u8 = 0x63;
32 pub const OP_NOTIF: u8 = 0x64;
33 pub const OP_ELSE: u8 = 0x67;
34 pub const OP_ENDIF: u8 = 0x68;
35 pub const OP_VERIFY: u8 = 0x69;
36 pub const OP_RETURN: u8 = 0x6A;
37 pub const OP_TOALTSTACK: u8 = 0x6B;
38 pub const OP_FROMALTSTACK: u8 = 0x6C;
39 pub const OP_IFDUP: u8 = 0x73;
40 pub const OP_DUP: u8 = 0x76;
41 pub const OP_SWAP: u8 = 0x7C;
42 pub const OP_EQUAL: u8 = 0x87;
43 pub const OP_EQUALVERIFY: u8 = 0x88;
44 pub const OP_SIZE: u8 = 0x82;
45 pub const OP_BOOLOR: u8 = 0x9B;
46 pub const OP_ADD: u8 = 0x93;
47 pub const OP_HASH160: u8 = 0xA9;
48 pub const OP_SHA256: u8 = 0xA8;
49 pub const OP_RIPEMD160: u8 = 0xA6;
50 pub const OP_HASH256: u8 = 0xAA;
51 pub const OP_CHECKSIG: u8 = 0xAC;
52 pub const OP_CHECKSIGVERIFY: u8 = 0xAD;
53 pub const OP_CHECKMULTISIG: u8 = 0xAE;
54 pub const OP_CHECKMULTISIGVERIFY: u8 = 0xAF;
55 pub const OP_CLTV: u8 = 0xB1;
56 pub const OP_CSV: u8 = 0xB2;
57}
58
59#[derive(Debug, Clone, PartialEq, Eq)]
68pub enum Policy {
69 Key([u8; 33]),
71 After(u32),
73 Older(u32),
75 Sha256([u8; 32]),
77 Ripemd160([u8; 20]),
79 Hash160([u8; 20]),
81 And(Vec<Policy>),
83 Or(Vec<Policy>),
85 Threshold(usize, Vec<Policy>),
87 Unsatisfiable,
89 Trivial,
91}
92
93#[allow(clippy::expect_used)]
94impl Policy {
95 pub fn compile(&self) -> Result<Miniscript, SignerError> {
100 match self {
101 Policy::Key(pk) => Ok(Miniscript::Pk(*pk)),
102 Policy::After(n) => Ok(Miniscript::After(*n)),
103 Policy::Older(n) => Ok(Miniscript::Older(*n)),
104 Policy::Sha256(h) => Ok(Miniscript::Sha256(*h)),
105 Policy::Ripemd160(h) => Ok(Miniscript::Ripemd160(*h)),
106 Policy::Hash160(h) => Ok(Miniscript::Hash160(*h)),
107 Policy::Unsatisfiable => Ok(Miniscript::False),
108 Policy::Trivial => Ok(Miniscript::True),
109
110 Policy::And(subs) => {
111 if subs.is_empty() {
112 return Err(SignerError::ParseError("empty AND policy".into()));
113 }
114 let compiled: Vec<Miniscript> =
115 subs.iter()
116 .map(|p| p.compile())
117 .collect::<Result<Vec<_>, _>>()?;
118 let mut iter = compiled.into_iter();
119 let first = iter.next().expect("non-empty");
120 Ok(iter.fold(first, |acc, ms| {
121 Miniscript::AndV(Box::new(acc), Box::new(ms))
122 }))
123 }
124
125 Policy::Or(subs) => {
126 if subs.is_empty() {
127 return Err(SignerError::ParseError("empty OR policy".into()));
128 }
129 let compiled: Vec<Miniscript> =
130 subs.iter()
131 .map(|p| p.compile())
132 .collect::<Result<Vec<_>, _>>()?;
133 let mut iter = compiled.into_iter();
134 let first = iter.next().expect("non-empty");
135 Ok(iter.fold(first, |acc, ms| {
136 Miniscript::OrB(Box::new(acc), Box::new(ms))
137 }))
138 }
139
140 Policy::Threshold(k, subs) => {
141 let k = *k;
142 if subs.is_empty() {
143 return Err(SignerError::ParseError("empty threshold".into()));
144 }
145 if k == 0 || k > subs.len() {
146 return Err(SignerError::ParseError(format!(
147 "invalid threshold {k} of {}",
148 subs.len()
149 )));
150 }
151
152 let all_keys = subs.iter().all(|p| matches!(p, Policy::Key(_)));
154 if all_keys && subs.len() <= 20 {
155 let keys: Vec<[u8; 33]> = subs
156 .iter()
157 .map(|p| match p {
158 Policy::Key(k) => *k,
159 _ => unreachable!(),
160 })
161 .collect();
162 return Ok(Miniscript::ThreshM(k, keys));
163 }
164
165 if k == subs.len() {
167 Policy::And(subs.clone()).compile()
169 } else if k == 1 {
170 Policy::Or(subs.clone()).compile()
172 } else {
173 let compiled: Vec<Miniscript> = subs
175 .iter()
176 .map(|p| p.compile())
177 .collect::<Result<Vec<_>, _>>()?;
178 Ok(Miniscript::Thresh(k, compiled))
179 }
180 }
181 }
182 }
183}
184
185#[derive(Debug, Clone, PartialEq, Eq)]
191pub enum Miniscript {
192 Pk([u8; 33]),
194 PkH([u8; 20]),
196 Older(u32),
198 After(u32),
200 Sha256([u8; 32]),
202 Ripemd160([u8; 20]),
204 Hash160([u8; 20]),
206 AndV(Box<Miniscript>, Box<Miniscript>),
208 OrB(Box<Miniscript>, Box<Miniscript>),
210 OrI(Box<Miniscript>, Box<Miniscript>),
212 ThreshM(usize, Vec<[u8; 33]>),
214 Thresh(usize, Vec<Miniscript>),
216 True,
218 False,
220}
221
222impl Miniscript {
223 #[must_use]
225 pub fn encode(&self) -> Vec<u8> {
226 let mut script = Vec::new();
227 self.encode_into(&mut script);
228 script
229 }
230
231 fn encode_into(&self, s: &mut Vec<u8>) {
233 match self {
234 Miniscript::Pk(key) => {
235 s.push(33); s.extend_from_slice(key);
237 s.push(op::OP_CHECKSIG);
238 }
239
240 Miniscript::PkH(hash) => {
241 s.push(op::OP_DUP);
242 s.push(op::OP_HASH160);
243 s.push(20);
244 s.extend_from_slice(hash);
245 s.push(op::OP_EQUALVERIFY);
246 s.push(op::OP_CHECKSIG);
247 }
248
249 Miniscript::Older(n) => {
250 push_script_number(s, *n as i64);
251 s.push(op::OP_CSV);
252 s.push(op::OP_VERIFY);
253 }
254
255 Miniscript::After(n) => {
256 push_script_number(s, *n as i64);
257 s.push(op::OP_CLTV);
258 s.push(op::OP_VERIFY);
259 }
260
261 Miniscript::Sha256(hash) => {
262 s.push(op::OP_SIZE);
263 push_script_number(s, 32);
264 s.push(op::OP_EQUALVERIFY);
265 s.push(op::OP_SHA256);
266 s.push(32);
267 s.extend_from_slice(hash);
268 s.push(op::OP_EQUAL);
269 }
270
271 Miniscript::Ripemd160(hash) => {
272 s.push(op::OP_SIZE);
273 push_script_number(s, 32);
274 s.push(op::OP_EQUALVERIFY);
275 s.push(op::OP_RIPEMD160);
276 s.push(20);
277 s.extend_from_slice(hash);
278 s.push(op::OP_EQUAL);
279 }
280
281 Miniscript::Hash160(hash) => {
282 s.push(op::OP_SIZE);
283 push_script_number(s, 32);
284 s.push(op::OP_EQUALVERIFY);
285 s.push(op::OP_HASH160);
286 s.push(20);
287 s.extend_from_slice(hash);
288 s.push(op::OP_EQUAL);
289 }
290
291 Miniscript::AndV(left, right) => {
292 left.encode_into(s);
293 right.encode_into(s);
294 }
295
296 Miniscript::OrB(left, right) => {
297 left.encode_into(s);
298 right.encode_into(s);
299 s.push(op::OP_BOOLOR);
300 }
301
302 Miniscript::OrI(left, right) => {
303 s.push(op::OP_IF);
304 left.encode_into(s);
305 s.push(op::OP_ELSE);
306 right.encode_into(s);
307 s.push(op::OP_ENDIF);
308 }
309
310 Miniscript::ThreshM(k, keys) => {
311 push_script_number(s, *k as i64);
312 for key in keys {
313 s.push(33);
314 s.extend_from_slice(key);
315 }
316 push_script_number(s, keys.len() as i64);
317 s.push(op::OP_CHECKMULTISIG);
318 }
319
320 Miniscript::Thresh(k, subs) => {
321 if let Some((first, rest)) = subs.split_first() {
322 first.encode_into(s);
323 for sub in rest {
324 sub.encode_into(s);
325 s.push(op::OP_ADD);
326 }
327 push_script_number(s, *k as i64);
328 s.push(op::OP_EQUAL);
329 }
330 }
331
332 Miniscript::True => {
333 s.push(0x51); }
335
336 Miniscript::False => {
337 s.push(op::OP_0);
338 }
339 }
340 }
341
342 pub fn script_size(&self) -> usize {
344 self.encode().len()
345 }
346
347 pub fn max_satisfaction_witness_elements(&self) -> usize {
349 match self {
350 Miniscript::Pk(_) => 1, Miniscript::PkH(_) => 2, Miniscript::Older(_) | Miniscript::After(_) => 0, Miniscript::Sha256(_) | Miniscript::Ripemd160(_) | Miniscript::Hash160(_) => 1, Miniscript::AndV(l, r) => {
355 l.max_satisfaction_witness_elements() + r.max_satisfaction_witness_elements()
356 }
357 Miniscript::OrB(l, r) | Miniscript::OrI(l, r) => {
358 l.max_satisfaction_witness_elements()
359 .max(r.max_satisfaction_witness_elements())
360 + 1 }
362 Miniscript::ThreshM(k, _) => k + 1, Miniscript::Thresh(_, subs) => subs
364 .iter()
365 .map(|s| s.max_satisfaction_witness_elements())
366 .sum::<usize>(),
367 Miniscript::True | Miniscript::False => 0,
368 }
369 }
370
371 pub fn max_satisfaction_size(&self) -> usize {
373 match self {
374 Miniscript::Pk(_) => 73, Miniscript::PkH(_) => 73 + 34, Miniscript::Older(_) | Miniscript::After(_) => 0,
377 Miniscript::Sha256(_) => 33, Miniscript::Ripemd160(_) | Miniscript::Hash160(_) => 33,
379 Miniscript::AndV(l, r) => l.max_satisfaction_size() + r.max_satisfaction_size(),
380 Miniscript::OrB(l, r) | Miniscript::OrI(l, r) => {
381 l.max_satisfaction_size().max(r.max_satisfaction_size()) + 1
382 }
383 Miniscript::ThreshM(k, _) => 1 + k * 73, Miniscript::Thresh(_, subs) => subs
385 .iter()
386 .map(|s| s.max_satisfaction_size())
387 .sum::<usize>(),
388 Miniscript::True | Miniscript::False => 0,
389 }
390 }
391}
392
393fn push_script_number(script: &mut Vec<u8>, n: i64) {
399 if n == 0 {
400 script.push(0x00); return;
402 }
403 if (1..=16).contains(&n) {
404 script.push(0x50 + n as u8); return;
406 }
407
408 let negative = n < 0;
409 let mut abs_n = if negative { (-n) as u64 } else { n as u64 };
410 let mut bytes = Vec::new();
411
412 while abs_n > 0 {
413 bytes.push((abs_n & 0xFF) as u8);
414 abs_n >>= 8;
415 }
416
417 if bytes.last().is_some_and(|b| b & 0x80 != 0) {
418 bytes.push(if negative { 0x80 } else { 0x00 });
419 } else if negative {
420 let last = bytes.len() - 1;
421 bytes[last] |= 0x80;
422 }
423
424 script.push(bytes.len() as u8);
425 script.extend_from_slice(&bytes);
426}
427
428#[cfg(test)]
433#[allow(clippy::unwrap_used, clippy::expect_used)]
434mod tests {
435 use super::*;
436
437 const KEY1: [u8; 33] = [0x02; 33];
438 const KEY2: [u8; 33] = [0x03; 33];
439 const KEY3: [u8; 33] = [0x04; 33];
440 const HASH32: [u8; 32] = [0xAA; 32];
441 const HASH20: [u8; 20] = [0xBB; 20];
442
443 #[test]
446 fn test_policy_key_compiles() {
447 let ms = Policy::Key(KEY1).compile().unwrap();
448 assert!(matches!(ms, Miniscript::Pk(_)));
449 }
450
451 #[test]
452 fn test_policy_after_compiles() {
453 let ms = Policy::After(500_000).compile().unwrap();
454 assert!(matches!(ms, Miniscript::After(500_000)));
455 }
456
457 #[test]
458 fn test_policy_older_compiles() {
459 let ms = Policy::Older(144).compile().unwrap();
460 assert!(matches!(ms, Miniscript::Older(144)));
461 }
462
463 #[test]
464 fn test_policy_sha256_compiles() {
465 let ms = Policy::Sha256(HASH32).compile().unwrap();
466 assert!(matches!(ms, Miniscript::Sha256(_)));
467 }
468
469 #[test]
470 fn test_policy_ripemd160_compiles() {
471 let ms = Policy::Ripemd160(HASH20).compile().unwrap();
472 assert!(matches!(ms, Miniscript::Ripemd160(_)));
473 }
474
475 #[test]
476 fn test_policy_hash160_compiles() {
477 let ms = Policy::Hash160(HASH20).compile().unwrap();
478 assert!(matches!(ms, Miniscript::Hash160(_)));
479 }
480
481 #[test]
482 fn test_policy_trivial() {
483 let ms = Policy::Trivial.compile().unwrap();
484 assert!(matches!(ms, Miniscript::True));
485 }
486
487 #[test]
488 fn test_policy_unsatisfiable() {
489 let ms = Policy::Unsatisfiable.compile().unwrap();
490 assert!(matches!(ms, Miniscript::False));
491 }
492
493 #[test]
494 fn test_policy_and_two_keys() {
495 let policy = Policy::And(vec![Policy::Key(KEY1), Policy::Key(KEY2)]);
496 let ms = policy.compile().unwrap();
497 assert!(matches!(ms, Miniscript::AndV(_, _)));
498 }
499
500 #[test]
501 fn test_policy_or_two_keys() {
502 let policy = Policy::Or(vec![Policy::Key(KEY1), Policy::Key(KEY2)]);
503 let ms = policy.compile().unwrap();
504 assert!(matches!(ms, Miniscript::OrB(_, _)));
505 }
506
507 #[test]
508 fn test_policy_threshold_all_keys() {
509 let policy = Policy::Threshold(
510 2,
511 vec![Policy::Key(KEY1), Policy::Key(KEY2), Policy::Key(KEY3)],
512 );
513 let ms = policy.compile().unwrap();
514 assert!(matches!(ms, Miniscript::ThreshM(2, _)));
515 }
516
517 #[test]
518 fn test_policy_threshold_n_of_n() {
519 let policy = Policy::Threshold(2, vec![Policy::Key(KEY1), Policy::Key(KEY2)]);
520 let ms = policy.compile().unwrap();
521 assert!(matches!(ms, Miniscript::ThreshM(2, _)));
523 }
524
525 #[test]
528 fn test_policy_empty_and_errors() {
529 assert!(Policy::And(vec![]).compile().is_err());
530 }
531
532 #[test]
533 fn test_policy_empty_or_errors() {
534 assert!(Policy::Or(vec![]).compile().is_err());
535 }
536
537 #[test]
538 fn test_policy_empty_threshold_errors() {
539 assert!(Policy::Threshold(1, vec![]).compile().is_err());
540 }
541
542 #[test]
543 fn test_policy_threshold_k_zero_errors() {
544 assert!(Policy::Threshold(0, vec![Policy::Key(KEY1)])
545 .compile()
546 .is_err());
547 }
548
549 #[test]
550 fn test_policy_threshold_k_exceeds_n_errors() {
551 assert!(
552 Policy::Threshold(3, vec![Policy::Key(KEY1), Policy::Key(KEY2)])
553 .compile()
554 .is_err()
555 );
556 }
557
558 #[test]
561 fn test_pk_script_structure() {
562 let script = Miniscript::Pk(KEY1).encode();
563 assert_eq!(script[0], 33); assert_eq!(&script[1..34], &KEY1);
565 assert_eq!(script[34], op::OP_CHECKSIG);
566 assert_eq!(script.len(), 35);
567 }
568
569 #[test]
570 fn test_pkh_script_structure() {
571 let script = Miniscript::PkH(HASH20).encode();
572 assert_eq!(script[0], op::OP_DUP);
573 assert_eq!(script[1], op::OP_HASH160);
574 assert_eq!(script[2], 20);
575 assert_eq!(&script[3..23], &HASH20);
576 assert_eq!(script[23], op::OP_EQUALVERIFY);
577 assert_eq!(script[24], op::OP_CHECKSIG);
578 }
579
580 #[test]
581 fn test_after_script_contains_cltv() {
582 let script = Miniscript::After(500_000).encode();
583 assert!(script.contains(&op::OP_CLTV));
584 assert!(script.contains(&op::OP_VERIFY));
585 }
586
587 #[test]
588 fn test_older_script_contains_csv() {
589 let script = Miniscript::Older(144).encode();
590 assert!(script.contains(&op::OP_CSV));
591 assert!(script.contains(&op::OP_VERIFY));
592 }
593
594 #[test]
595 fn test_sha256_script_contains_hash_op() {
596 let script = Miniscript::Sha256(HASH32).encode();
597 assert!(script.contains(&op::OP_SHA256));
598 assert!(script.contains(&op::OP_EQUAL));
599 assert!(script.contains(&op::OP_SIZE));
600 }
601
602 #[test]
603 fn test_ripemd160_script_contains_hash_op() {
604 let script = Miniscript::Ripemd160(HASH20).encode();
605 assert!(script.contains(&op::OP_RIPEMD160));
606 }
607
608 #[test]
609 fn test_hash160_script_contains_hash_op() {
610 let script = Miniscript::Hash160(HASH20).encode();
611 assert!(script.contains(&op::OP_HASH160));
612 }
613
614 #[test]
615 fn test_thresh_m_2_of_3_script() {
616 let script = Miniscript::ThreshM(2, vec![KEY1, KEY2, KEY3]).encode();
617 assert_eq!(script[0], 0x52); assert_eq!(script[1], 33);
620 assert_eq!(script[1 + 34], 33);
621 assert_eq!(script[1 + 68], 33);
622 assert_eq!(script[1 + 102], 0x53);
624 assert_eq!(script[1 + 103], op::OP_CHECKMULTISIG);
626 }
627
628 #[test]
629 fn test_and_v_combines_scripts() {
630 let ms = Miniscript::AndV(
631 Box::new(Miniscript::Pk(KEY1)),
632 Box::new(Miniscript::Pk(KEY2)),
633 );
634 let script = ms.encode();
635 assert_eq!(script.len(), 35 + 35); }
638
639 #[test]
640 fn test_or_b_adds_boolor() {
641 let ms = Miniscript::OrB(
642 Box::new(Miniscript::Pk(KEY1)),
643 Box::new(Miniscript::Pk(KEY2)),
644 );
645 let script = ms.encode();
646 assert_eq!(*script.last().unwrap(), op::OP_BOOLOR);
647 }
648
649 #[test]
650 fn test_or_i_uses_if_else_endif() {
651 let ms = Miniscript::OrI(
652 Box::new(Miniscript::Pk(KEY1)),
653 Box::new(Miniscript::Pk(KEY2)),
654 );
655 let script = ms.encode();
656 assert_eq!(script[0], op::OP_IF);
657 assert!(script.contains(&op::OP_ELSE));
658 assert_eq!(*script.last().unwrap(), op::OP_ENDIF);
659 }
660
661 #[test]
662 fn test_true_encodes_op_1() {
663 let script = Miniscript::True.encode();
664 assert_eq!(script, vec![0x51]);
665 }
666
667 #[test]
668 fn test_false_encodes_op_0() {
669 let script = Miniscript::False.encode();
670 assert_eq!(script, vec![op::OP_0]);
671 }
672
673 #[test]
676 fn test_pk_script_size() {
677 assert_eq!(Miniscript::Pk(KEY1).script_size(), 35);
678 }
679
680 #[test]
681 fn test_thresh_m_script_size() {
682 let ms = Miniscript::ThreshM(2, vec![KEY1, KEY2, KEY3]);
683 assert_eq!(ms.script_size(), 105);
685 }
686
687 #[test]
690 fn test_pk_witness_elements() {
691 assert_eq!(Miniscript::Pk(KEY1).max_satisfaction_witness_elements(), 1);
692 }
693
694 #[test]
695 fn test_pkh_witness_elements() {
696 assert_eq!(
697 Miniscript::PkH(HASH20).max_satisfaction_witness_elements(),
698 2
699 );
700 }
701
702 #[test]
703 fn test_thresh_m_witness_elements() {
704 let ms = Miniscript::ThreshM(2, vec![KEY1, KEY2, KEY3]);
705 assert_eq!(ms.max_satisfaction_witness_elements(), 3); }
707
708 #[test]
709 fn test_pk_witness_size() {
710 assert_eq!(Miniscript::Pk(KEY1).max_satisfaction_size(), 73);
711 }
712
713 #[test]
714 fn test_thresh_m_witness_size() {
715 let ms = Miniscript::ThreshM(2, vec![KEY1, KEY2, KEY3]);
716 assert_eq!(ms.max_satisfaction_size(), 1 + 2 * 73); }
718
719 #[test]
720 fn test_sha256_witness_size() {
721 assert_eq!(Miniscript::Sha256(HASH32).max_satisfaction_size(), 33);
722 }
723
724 #[test]
727 fn test_e2e_2_of_3_policy_to_script() {
728 let policy = Policy::Threshold(
729 2,
730 vec![Policy::Key(KEY1), Policy::Key(KEY2), Policy::Key(KEY3)],
731 );
732 let ms = policy.compile().unwrap();
733 let script = ms.encode();
734 assert_eq!(*script.last().unwrap(), op::OP_CHECKMULTISIG);
736 assert!(script.len() > 100);
737 }
738
739 #[test]
740 fn test_e2e_key_and_timelock() {
741 let policy = Policy::And(vec![Policy::Key(KEY1), Policy::After(800_000)]);
742 let ms = policy.compile().unwrap();
743 let script = ms.encode();
744 assert!(script.contains(&op::OP_CHECKSIG));
745 assert!(script.contains(&op::OP_CLTV));
746 }
747
748 #[test]
749 fn test_e2e_htlc_like_policy() {
750 let policy = Policy::Or(vec![
752 Policy::And(vec![Policy::Sha256(HASH32), Policy::Key(KEY1)]),
753 Policy::And(vec![Policy::Key(KEY2), Policy::After(700_000)]),
754 ]);
755 let ms = policy.compile().unwrap();
756 let script = ms.encode();
757 assert!(script.len() > 50);
758 assert!(script.contains(&op::OP_SHA256));
759 assert!(script.contains(&op::OP_CLTV));
760 }
761
762 #[test]
765 fn test_push_script_number_zero() {
766 let mut s = Vec::new();
767 push_script_number(&mut s, 0);
768 assert_eq!(s, vec![0x00]);
769 }
770
771 #[test]
772 fn test_push_script_number_small() {
773 for n in 1..=16 {
774 let mut s = Vec::new();
775 push_script_number(&mut s, n);
776 assert_eq!(s, vec![0x50 + n as u8]);
777 }
778 }
779
780 #[test]
781 fn test_push_script_number_17() {
782 let mut s = Vec::new();
783 push_script_number(&mut s, 17);
784 assert_eq!(s, vec![1, 17]); }
786
787 #[test]
788 fn test_push_script_number_256() {
789 let mut s = Vec::new();
790 push_script_number(&mut s, 256);
791 assert_eq!(s, vec![2, 0x00, 0x01]); }
793}