@@ -31,11 +31,13 @@ use {
31
31
crate :: descriptor:: TapTree ,
32
32
crate :: miniscript:: ScriptContext ,
33
33
crate :: policy:: compiler:: CompilerError ,
34
+ crate :: policy:: compiler:: OrdF64 ,
34
35
crate :: policy:: { compiler, Concrete , Liftable , Semantic } ,
35
36
crate :: Descriptor ,
36
37
crate :: Miniscript ,
37
38
crate :: Tap ,
38
- std:: collections:: HashMap ,
39
+ std:: cmp:: Reverse ,
40
+ std:: collections:: { BinaryHeap , HashMap } ,
39
41
std:: sync:: Arc ,
40
42
} ;
41
43
@@ -173,15 +175,23 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
173
175
}
174
176
}
175
177
178
+ /// Compile [`Policy::Or`] and [`Policy::Threshold`] according to odds
176
179
#[ cfg( feature = "compiler" ) ]
177
- fn compile_leaf_taptree ( & self ) -> Result < TapTree < Pk > , Error > {
178
- let compilation = self . compile :: < Tap > ( ) . unwrap ( ) ;
179
- 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)
180
189
}
181
190
182
- /// Extract the Taproot internal_key from policy tree.
191
+ /// Extract the internal_key from policy tree.
183
192
#[ cfg( feature = "compiler" ) ]
184
193
fn extract_key ( self , unspendable_key : Option < Pk > ) -> Result < ( Pk , Policy < Pk > ) , Error > {
194
+ // Making sure the borrow ends before you move the value.
185
195
let mut internal_key: Option < Pk > = None ;
186
196
{
187
197
let mut prob = 0. ;
@@ -222,11 +232,28 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
222
232
}
223
233
}
224
234
225
- /// Compile the [`Tr`] descriptor into optimized [`TapTree`] implementation
235
+ /// Compile the [`Policy`] into a [`Tr`][`Descriptor::Tr`] Descriptor.
236
+ ///
237
+ /// ### TapTree compilation
238
+ ///
239
+ /// The policy tree constructed by root-level disjunctions over [`Or`][`Policy::Or`] and
240
+ /// [`Thresh`][`Policy::Threshold`](1, ..) which is flattened into a vector (with respective
241
+ /// probabilities derived from odds) of policies.
242
+ /// For example, the policy `thresh(1,or(pk(A),pk(B)),and(or(pk(C),pk(D)),pk(E)))` gives the vector
243
+ /// `[pk(A),pk(B),and(or(pk(C),pk(D)),pk(E)))]`. Each policy in the vector is compiled into
244
+ /// the respective miniscripts. A Huffman Tree is created from this vector which optimizes over
245
+ /// the probabilitity of satisfaction for the respective branch in the TapTree.
246
+ // TODO: We might require other compile errors for Taproot.
226
247
#[ cfg( feature = "compiler" ) ]
227
248
pub fn compile_tr ( & self , unspendable_key : Option < Pk > ) -> Result < Descriptor < Pk > , Error > {
228
249
let ( internal_key, policy) = self . clone ( ) . extract_key ( unspendable_key) ?;
229
- let tree = Descriptor :: new_tr ( internal_key, Some ( policy. compile_leaf_taptree ( ) ?) ) ?;
250
+ let tree = Descriptor :: new_tr (
251
+ internal_key,
252
+ match policy {
253
+ Policy :: Trivial => None ,
254
+ policy => Some ( policy. compile_tr_policy ( ) ?) ,
255
+ } ,
256
+ ) ?;
230
257
Ok ( tree)
231
258
}
232
259
@@ -780,3 +807,34 @@ where
780
807
Policy :: from_tree_prob ( top, false ) . map ( |( _, result) | result)
781
808
}
782
809
}
810
+
811
+ /// Create a Huffman Tree from compiled [Miniscript] nodes
812
+ #[ cfg( feature = "compiler" ) ]
813
+ fn with_huffman_tree < Pk : MiniscriptKey > (
814
+ ms : Vec < ( OrdF64 , Miniscript < Pk , Tap > ) > ,
815
+ ) -> Result < TapTree < Pk > , Error > {
816
+ let mut node_weights = BinaryHeap :: < ( Reverse < OrdF64 > , TapTree < Pk > ) > :: new ( ) ;
817
+ for ( prob, script) in ms {
818
+ node_weights. push ( ( Reverse ( prob) , TapTree :: Leaf ( Arc :: new ( script) ) ) ) ;
819
+ }
820
+ if node_weights. is_empty ( ) {
821
+ return Err ( errstr ( "Empty Miniscript compilation" ) ) ;
822
+ }
823
+ while node_weights. len ( ) > 1 {
824
+ let ( p1, s1) = node_weights. pop ( ) . expect ( "len must atleast be two" ) ;
825
+ let ( p2, s2) = node_weights. pop ( ) . expect ( "len must atleast be two" ) ;
826
+
827
+ let p = ( p1. 0 ) . 0 + ( p2. 0 ) . 0 ;
828
+ node_weights. push ( (
829
+ Reverse ( OrdF64 ( p) ) ,
830
+ TapTree :: Tree ( Arc :: from ( s1) , Arc :: from ( s2) ) ,
831
+ ) ) ;
832
+ }
833
+
834
+ debug_assert ! ( node_weights. len( ) == 1 ) ;
835
+ let node = node_weights
836
+ . pop ( )
837
+ . expect ( "huffman tree algorithm is broken" )
838
+ . 1 ;
839
+ Ok ( node)
840
+ }
0 commit comments