@@ -25,11 +25,13 @@ use {
25
25
crate :: descriptor:: TapTree ,
26
26
crate :: miniscript:: ScriptContext ,
27
27
crate :: policy:: compiler:: CompilerError ,
28
+ crate :: policy:: compiler:: OrdF64 ,
28
29
crate :: policy:: { compiler, Concrete , Liftable , Semantic } ,
29
30
crate :: Descriptor ,
30
31
crate :: Miniscript ,
31
32
crate :: Tap ,
32
- std:: collections:: HashMap ,
33
+ std:: cmp:: Reverse ,
34
+ std:: collections:: { BinaryHeap , HashMap } ,
33
35
std:: sync:: Arc ,
34
36
} ;
35
37
@@ -173,14 +175,20 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
173
175
}
174
176
}
175
177
176
- /// Single-Node compilation
178
+ /// Compile [`Policy::Or`] and [`Policy::Threshold`] according to odds
177
179
#[ 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)
181
189
}
182
190
183
- /// Extract the Taproot internal_key from policy tree.
191
+ /// Extract the internal_key from policy tree.
184
192
#[ cfg( feature = "compiler" ) ]
185
193
fn extract_key ( self , unspendable_key : Option < Pk > ) -> Result < ( Pk , Policy < Pk > ) , Error > {
186
194
let mut internal_key: Option < Pk > = None ;
@@ -223,11 +231,28 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
223
231
}
224
232
}
225
233
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.
227
246
#[ cfg( feature = "compiler" ) ]
228
247
pub fn compile_tr ( & self , unspendable_key : Option < Pk > ) -> Result < Descriptor < Pk > , Error > {
229
248
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
+ ) ?;
231
256
Ok ( tree)
232
257
}
233
258
@@ -772,3 +797,34 @@ where
772
797
Policy :: from_tree_prob ( top, false ) . map ( |( _, result) | result)
773
798
}
774
799
}
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