33//! Concrete Policies
44//!
55
6- use core:: { fmt, str} ;
6+ use core:: { cmp , fmt, str} ;
77#[ cfg( feature = "std" ) ]
88use std:: error;
99
@@ -24,6 +24,7 @@ use crate::expression::{self, FromTree};
2424use crate :: iter:: { Tree , TreeLike } ;
2525use crate :: miniscript:: types:: extra_props:: TimelockInfo ;
2626use crate :: prelude:: * ;
27+ use crate :: primitives:: locktime_consensus_cmp;
2728use crate :: sync:: Arc ;
2829#[ cfg( all( doc, not( feature = "compiler" ) ) ) ]
2930use crate :: Descriptor ;
@@ -41,7 +42,7 @@ const MAX_COMPILATION_LEAVES: usize = 1024;
4142// Currently the vectors in And/Or are limited to two elements, this is a general miniscript thing
4243// not specific to rust-miniscript. Eventually we would like to extend these to be n-ary, but first
4344// we need to decide on a game plan for how to efficiently compile n-ary disjunctions
44- #[ derive( Clone , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
45+ #[ derive( Clone , PartialEq , Eq , Hash ) ]
4546pub enum Policy < Pk : MiniscriptKey > {
4647 /// Unsatisfiable.
4748 Unsatisfiable ,
@@ -70,6 +71,57 @@ pub enum Policy<Pk: MiniscriptKey> {
7071 Thresh ( Threshold < Arc < Self > , 0 > ) ,
7172}
7273
74+ impl < Pk : MiniscriptKey > Policy < Pk > {
75+ fn variant_name ( & self ) -> & ' static str {
76+ match * self {
77+ Self :: Unsatisfiable => "unsatisfiable" ,
78+ Self :: Trivial => "trivial" ,
79+ Self :: Key ( _) => "key" ,
80+ Self :: After ( _) => "after" ,
81+ Self :: Older ( _) => "older" ,
82+ Self :: Sha256 ( _) => "sha256" ,
83+ Self :: Hash256 ( _) => "hash256" ,
84+ Self :: Ripemd160 ( _) => "ripemd160" ,
85+ Self :: Hash160 ( _) => "hash160" ,
86+ Self :: And ( _) => "and" ,
87+ Self :: Or ( _) => "or" ,
88+ Self :: Thresh ( _) => "thresh" ,
89+ }
90+ }
91+ }
92+
93+ impl < Pk : MiniscriptKey > PartialOrd for Policy < Pk > {
94+ fn partial_cmp ( & self , other : & Self ) -> Option < cmp:: Ordering > { Some ( self . cmp ( other) ) }
95+ }
96+
97+ impl < Pk : MiniscriptKey > Ord for Policy < Pk > {
98+ fn cmp ( & self , other : & Self ) -> cmp:: Ordering {
99+ match self . variant_name ( ) . cmp ( other. variant_name ( ) ) {
100+ cmp:: Ordering :: Equal => { }
101+ ord => return ord,
102+ }
103+ match ( self , other) {
104+ ( Self :: Unsatisfiable , Self :: Unsatisfiable ) => cmp:: Ordering :: Equal ,
105+ ( Self :: Trivial , Self :: Trivial ) => cmp:: Ordering :: Equal ,
106+ ( Self :: Key ( a) , Self :: Key ( b) ) => a. cmp ( b) ,
107+ ( Self :: After ( a) , Self :: After ( b) ) => {
108+ locktime_consensus_cmp ( a. to_consensus_u32 ( ) , b. to_consensus_u32 ( ) )
109+ }
110+ ( Self :: Older ( a) , Self :: Older ( b) ) => {
111+ locktime_consensus_cmp ( a. to_consensus_u32 ( ) , b. to_consensus_u32 ( ) )
112+ }
113+ ( Self :: Sha256 ( a) , Self :: Sha256 ( b) ) => a. cmp ( b) ,
114+ ( Self :: Hash256 ( a) , Self :: Hash256 ( b) ) => a. cmp ( b) ,
115+ ( Self :: Ripemd160 ( a) , Self :: Ripemd160 ( b) ) => a. cmp ( b) ,
116+ ( Self :: Hash160 ( a) , Self :: Hash160 ( b) ) => a. cmp ( b) ,
117+ ( Self :: And ( a) , Self :: And ( b) ) => a. cmp ( b) ,
118+ ( Self :: Or ( a) , Self :: Or ( b) ) => a. cmp ( b) ,
119+ ( Self :: Thresh ( a) , Self :: Thresh ( b) ) => a. cmp ( b) ,
120+ _ => unreachable ! ( "variant_name ensures same variant" ) ,
121+ }
122+ }
123+ }
124+
73125/// Detailed error type for concrete policies.
74126#[ derive( Copy , Clone , PartialEq , Eq , Debug , Hash ) ]
75127pub enum PolicyError {
@@ -1389,4 +1441,26 @@ mod tests {
13891441 . parse :: < Policy < String > > ( )
13901442 . unwrap_err ( ) ;
13911443 }
1444+
1445+ #[ test]
1446+ fn policy_ord_is_consistent ( ) {
1447+ // Same direction as numeric — passes under both old and new scheme.
1448+ let a = Policy :: < String > :: from_str ( "after(100)" ) . unwrap ( ) ;
1449+ let b = Policy :: < String > :: from_str ( "after(200)" ) . unwrap ( ) ;
1450+ assert ! ( a < b, "after(100) should be less than after(200)" ) ;
1451+
1452+ // Cross-variant: must not be equal.
1453+ let c = Policy :: < String > :: from_str ( "older(100)" ) . unwrap ( ) ;
1454+ assert ! ( a != c, "after and older variants must not compare equal" ) ;
1455+
1456+ // Numeric consensus ordering: 9 < 10.
1457+ let d = Policy :: < String > :: from_str ( "after(9)" ) . unwrap ( ) ;
1458+ let e = Policy :: < String > :: from_str ( "after(10)" ) . unwrap ( ) ;
1459+ assert ! ( d < e, "after(9) < after(10) under consensus u32 ordering" ) ;
1460+
1461+ // "trivial" < "unsatisfiable" alphabetically.
1462+ let trivial = Policy :: < String > :: from_str ( "TRIVIAL" ) . unwrap ( ) ;
1463+ let unsat = Policy :: < String > :: from_str ( "UNSATISFIABLE" ) . unwrap ( ) ;
1464+ assert ! ( trivial < unsat, "trivial < unsatisfiable under variant_name ordering" ) ;
1465+ }
13921466}
0 commit comments