@@ -25,11 +25,13 @@ use {
2525 crate :: descriptor:: TapTree ,
2626 crate :: miniscript:: ScriptContext ,
2727 crate :: policy:: compiler:: CompilerError ,
28+ crate :: policy:: compiler:: OrdF64 ,
2829 crate :: policy:: { compiler, Concrete , Liftable , Semantic } ,
2930 crate :: Descriptor ,
3031 crate :: Miniscript ,
3132 crate :: Tap ,
32- std:: collections:: HashMap ,
33+ std:: cmp:: Reverse ,
34+ std:: collections:: { BinaryHeap , HashMap } ,
3335 std:: sync:: Arc ,
3436} ;
3537
@@ -173,14 +175,20 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
173175 }
174176 }
175177
176- /// Single-Node compilation
178+ /// Compile [`Policy::Or`] and [`Policy::Threshold`] according to odds
177179 #[ cfg( feature = "compiler" ) ]
178- fn compile_leaf_taptree ( & self ) -> Result < TapTree < Pk > , Error > {
179- let compilation = self . compile :: < Tap > ( ) . unwrap ( ) ;
180- Ok ( TapTree :: Leaf ( Arc :: new ( compilation) ) )
180+ fn compile_tr_policy ( & self ) -> Result < TapTree < Pk > , Error > {
181+ let leaf_compilations: Vec < _ > = self
182+ . to_tapleaf_prob_vec ( 1.0 )
183+ . into_iter ( )
184+ . filter ( |x| x. 1 != Policy :: Unsatisfiable )
185+ . map ( |( prob, ref policy) | ( OrdF64 ( prob) , compiler:: best_compilation ( policy) . unwrap ( ) ) )
186+ . collect ( ) ;
187+ let taptree = with_huffman_tree :: < Pk > ( leaf_compilations) . unwrap ( ) ;
188+ Ok ( taptree)
181189 }
182190
183- /// Extract the Taproot internal_key from policy tree.
191+ /// Extract the internal_key from policy tree.
184192 #[ cfg( feature = "compiler" ) ]
185193 fn extract_key ( self , unspendable_key : Option < Pk > ) -> Result < ( Pk , Policy < Pk > ) , Error > {
186194 let mut internal_key: Option < Pk > = None ;
@@ -223,11 +231,28 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
223231 }
224232 }
225233
226- /// Compile the [`Tr`] descriptor into optimized [`TapTree`] implementation
234+ /// Compile the [`Policy`] into a [`Tr`][`Descriptor::Tr`] Descriptor.
235+ ///
236+ /// ### TapTree compilation
237+ ///
238+ /// The policy tree constructed by root-level disjunctions over [`Or`][`Policy::Or`] and
239+ /// [`Thresh`][`Policy::Threshold`](1, ..) which is flattened into a vector (with respective
240+ /// probabilities derived from odds) of policies.
241+ /// For example, the policy `thresh(1,or(pk(A),pk(B)),and(or(pk(C),pk(D)),pk(E)))` gives the vector
242+ /// `[pk(A),pk(B),and(or(pk(C),pk(D)),pk(E)))]`. Each policy in the vector is compiled into
243+ /// the respective miniscripts. A Huffman Tree is created from this vector which optimizes over
244+ /// the probabilitity of satisfaction for the respective branch in the TapTree.
245+ // TODO: We might require other compile errors for Taproot.
227246 #[ cfg( feature = "compiler" ) ]
228247 pub fn compile_tr ( & self , unspendable_key : Option < Pk > ) -> Result < Descriptor < Pk > , Error > {
229248 let ( internal_key, policy) = self . clone ( ) . extract_key ( unspendable_key) ?;
230- let tree = Descriptor :: new_tr ( internal_key, Some ( policy. compile_leaf_taptree ( ) ?) ) ?;
249+ let tree = Descriptor :: new_tr (
250+ internal_key,
251+ match policy {
252+ Policy :: Trivial => None ,
253+ policy => Some ( policy. compile_tr_policy ( ) ?) ,
254+ } ,
255+ ) ?;
231256 Ok ( tree)
232257 }
233258
@@ -772,3 +797,34 @@ where
772797 Policy :: from_tree_prob ( top, false ) . map ( |( _, result) | result)
773798 }
774799}
800+
801+ /// Create a Huffman Tree from compiled [Miniscript] nodes
802+ #[ cfg( feature = "compiler" ) ]
803+ fn with_huffman_tree < Pk : MiniscriptKey > (
804+ ms : Vec < ( OrdF64 , Miniscript < Pk , Tap > ) > ,
805+ ) -> Result < TapTree < Pk > , Error > {
806+ let mut node_weights = BinaryHeap :: < ( Reverse < OrdF64 > , TapTree < Pk > ) > :: new ( ) ;
807+ for ( prob, script) in ms {
808+ node_weights. push ( ( Reverse ( prob) , TapTree :: Leaf ( Arc :: new ( script) ) ) ) ;
809+ }
810+ if node_weights. is_empty ( ) {
811+ return Err ( errstr ( "Empty Miniscript compilation" ) ) ;
812+ }
813+ while node_weights. len ( ) > 1 {
814+ let ( p1, s1) = node_weights. pop ( ) . expect ( "len must atleast be two" ) ;
815+ let ( p2, s2) = node_weights. pop ( ) . expect ( "len must atleast be two" ) ;
816+
817+ let p = ( p1. 0 ) . 0 + ( p2. 0 ) . 0 ;
818+ node_weights. push ( (
819+ Reverse ( OrdF64 ( p) ) ,
820+ TapTree :: Tree ( Arc :: from ( s1) , Arc :: from ( s2) ) ,
821+ ) ) ;
822+ }
823+
824+ debug_assert ! ( node_weights. len( ) == 1 ) ;
825+ let node = node_weights
826+ . pop ( )
827+ . expect ( "huffman tree algorithm is broken" )
828+ . 1 ;
829+ Ok ( node)
830+ }
0 commit comments