@@ -248,6 +248,13 @@ impl<Pk: MiniscriptKey> fmt::Debug for Policy<Pk> {
248248 }
249249}
250250
251+ /// Displays the policy using mathematical notation for readability.
252+ ///
253+ /// - `and(a, b)` is displayed as `(a ∧ b)`
254+ /// - `or(a, b)` is displayed as `(a ∨ b)`
255+ /// - `thresh(k, a, b, c)` is displayed as `#{a, b, c} = k`
256+ ///
257+ /// Note: this format is not parseable.
251258impl < Pk : MiniscriptKey > fmt:: Display for Policy < Pk > {
252259 fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
253260 match * self {
@@ -261,13 +268,90 @@ impl<Pk: MiniscriptKey> fmt::Display for Policy<Pk> {
261268 Policy :: Ripemd160 ( ref h) => write ! ( f, "ripemd160({})" , h) ,
262269 Policy :: Hash160 ( ref h) => write ! ( f, "hash160({})" , h) ,
263270 Policy :: Thresh ( ref thresh) => {
271+ let mut iter = thresh. iter ( ) ;
272+ let first = iter. next ( ) . expect ( "thresholds are never empty" ) ;
264273 if thresh. k ( ) == thresh. n ( ) {
265- thresh. display ( "and" , false ) . fmt ( f)
274+ write ! ( f, "({}" , first) ?;
275+ for sub in iter {
276+ write ! ( f, " ∧ {}" , sub) ?;
277+ }
278+ f. write_str ( ")" )
279+ } else if thresh. k ( ) == 1 {
280+ write ! ( f, "({}" , first) ?;
281+ for sub in iter {
282+ write ! ( f, " ∨ {}" , sub) ?;
283+ }
284+ f. write_str ( ")" )
285+ } else {
286+ write ! ( f, "#{{{}" , first) ?;
287+ for sub in iter {
288+ write ! ( f, ", {}" , sub) ?;
289+ }
290+ write ! ( f, "}} = {}" , thresh. k( ) )
291+ }
292+ }
293+ }
294+ }
295+ }
296+
297+ impl < Pk : MiniscriptKey > Policy < Pk > {
298+ /// Serializes the policy using function-call notation
299+ /// (`and(..)`, `or(..)`, `thresh(k, ..)`).
300+ ///
301+ /// The [`fmt::Display`] impl uses mathematical notation (`∧`, `∨`,
302+ /// `#{..} = k`). That change is deliberate: Display is no longer intended
303+ /// to be a stable, machine-readable serialization of a semantic policy.
304+ /// New code should treat Display as presentation-only.
305+ ///
306+ /// This method exists for the narrow case of cross-version comparison
307+ /// against older releases of this crate (notably the
308+ /// `regression_descriptor_parse` fuzz target), which still emit the
309+ /// function-call form. Prefer working with the structured [`Policy`]
310+ /// value directly over parsing this string.
311+ pub fn to_policy_syntax_string ( & self ) -> String {
312+ let mut s = String :: new ( ) ;
313+ self . write_policy_syntax ( & mut s)
314+ . expect ( "writing to a String is infallible" ) ;
315+ s
316+ }
317+
318+ fn write_policy_syntax < W : fmt:: Write > ( & self , w : & mut W ) -> fmt:: Result {
319+ match * self {
320+ Policy :: Unsatisfiable => w. write_str ( "UNSATISFIABLE" ) ,
321+ Policy :: Trivial => w. write_str ( "TRIVIAL" ) ,
322+ Policy :: Key ( ref pkh) => write ! ( w, "pk({})" , pkh) ,
323+ Policy :: After ( n) => write ! ( w, "after({})" , n) ,
324+ Policy :: Older ( n) => write ! ( w, "older({})" , n) ,
325+ Policy :: Sha256 ( ref h) => write ! ( w, "sha256({})" , h) ,
326+ Policy :: Hash256 ( ref h) => write ! ( w, "hash256({})" , h) ,
327+ Policy :: Ripemd160 ( ref h) => write ! ( w, "ripemd160({})" , h) ,
328+ Policy :: Hash160 ( ref h) => write ! ( w, "hash160({})" , h) ,
329+ Policy :: Thresh ( ref thresh) => {
330+ let ( name, show_k) = if thresh. k ( ) == thresh. n ( ) {
331+ ( "and" , false )
266332 } else if thresh. k ( ) == 1 {
267- thresh. display ( "or" , false ) . fmt ( f)
333+ ( "or" , false )
334+ } else {
335+ ( "thresh" , true )
336+ } ;
337+ w. write_str ( name) ?;
338+ w. write_str ( "(" ) ?;
339+ let mut iter = thresh. iter ( ) ;
340+ if show_k {
341+ write ! ( w, "{}" , thresh. k( ) ) ?;
342+ for child in iter {
343+ w. write_str ( "," ) ?;
344+ child. write_policy_syntax ( w) ?;
345+ }
268346 } else {
269- thresh. display ( "thresh" , true ) . fmt ( f)
347+ let first = iter. next ( ) . expect ( "thresholds are never empty" ) ;
348+ first. write_policy_syntax ( w) ?;
349+ for child in iter {
350+ w. write_str ( "," ) ?;
351+ child. write_policy_syntax ( w) ?;
352+ }
270353 }
354+ w. write_str ( ")" )
271355 }
272356 }
273357 }
@@ -281,8 +365,6 @@ impl<Pk: FromStrKey> str::FromStr for Policy<Pk> {
281365 }
282366}
283367
284- serde_string_impl_pk ! ( Policy , "a miniscript semantic policy" ) ;
285-
286368impl < Pk : FromStrKey > expression:: FromTree for Policy < Pk > {
287369 fn from_tree ( root : expression:: TreeIterItem ) -> Result < Policy < Pk > , Error > {
288370 root. verify_no_curly_braces ( )
0 commit comments