@@ -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
@@ -157,15 +159,23 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
157
159
}
158
160
}
159
161
162
+ /// Compile [`Policy::Or`] and [`Policy::Threshold`] according to odds
160
163
#[ cfg( feature = "compiler" ) ]
161
- fn compile_leaf_taptree ( & self ) -> Result < TapTree < Pk > , Error > {
162
- let compilation = self . compile :: < Tap > ( ) . unwrap ( ) ;
163
- Ok ( TapTree :: Leaf ( Arc :: new ( compilation) ) )
164
+ fn compile_tr_policy ( & self ) -> Result < TapTree < Pk > , Error > {
165
+ let leaf_compilations: Vec < _ > = self
166
+ . to_tapleaf_prob_vec ( 1.0 )
167
+ . into_iter ( )
168
+ . filter ( |x| x. 1 != Policy :: Unsatisfiable )
169
+ . map ( |( prob, ref policy) | ( OrdF64 ( prob) , compiler:: best_compilation ( policy) . unwrap ( ) ) )
170
+ . collect ( ) ;
171
+ let taptree = with_huffman_tree :: < Pk > ( leaf_compilations) . unwrap ( ) ;
172
+ Ok ( taptree)
164
173
}
165
174
166
- /// Extract the Taproot internal_key from policy tree.
175
+ /// Extract the internal_key from policy tree.
167
176
#[ cfg( feature = "compiler" ) ]
168
177
fn extract_key ( self , unspendable_key : Option < Pk > ) -> Result < ( Pk , Policy < Pk > ) , Error > {
178
+ // Making sure the borrow ends before you move the value.
169
179
let mut internal_key: Option < Pk > = None ;
170
180
{
171
181
let mut prob = 0. ;
@@ -206,11 +216,28 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
206
216
}
207
217
}
208
218
209
- /// Compile the [`Tr`] descriptor into optimized [`TapTree`] implementation
219
+ /// Compile the [`Policy`] into a [`Tr`][`Descriptor::Tr`] Descriptor.
220
+ ///
221
+ /// ### TapTree compilation
222
+ ///
223
+ /// The policy tree constructed by root-level disjunctions over [`Or`][`Policy::Or`] and
224
+ /// [`Thresh`][`Policy::Threshold`](1, ..) which is flattened into a vector (with respective
225
+ /// probabilities derived from odds) of policies.
226
+ /// For example, the policy `thresh(1,or(pk(A),pk(B)),and(or(pk(C),pk(D)),pk(E)))` gives the vector
227
+ /// `[pk(A),pk(B),and(or(pk(C),pk(D)),pk(E)))]`. Each policy in the vector is compiled into
228
+ /// the respective miniscripts. A Huffman Tree is created from this vector which optimizes over
229
+ /// the probabilitity of satisfaction for the respective branch in the TapTree.
230
+ // TODO: We might require other compile errors for Taproot.
210
231
#[ cfg( feature = "compiler" ) ]
211
232
pub fn compile_tr ( & self , unspendable_key : Option < Pk > ) -> Result < Descriptor < Pk > , Error > {
212
233
let ( internal_key, policy) = self . clone ( ) . extract_key ( unspendable_key) ?;
213
- let tree = Descriptor :: new_tr ( internal_key, Some ( policy. compile_leaf_taptree ( ) ?) ) ?;
234
+ let tree = Descriptor :: new_tr (
235
+ internal_key,
236
+ match policy {
237
+ Policy :: Trivial => None ,
238
+ policy => Some ( policy. compile_tr_policy ( ) ?) ,
239
+ } ,
240
+ ) ?;
214
241
Ok ( tree)
215
242
}
216
243
@@ -764,3 +791,34 @@ where
764
791
Policy :: from_tree_prob ( top, false ) . map ( |( _, result) | result)
765
792
}
766
793
}
794
+
795
+ /// Create a Huffman Tree from compiled [Miniscript] nodes
796
+ #[ cfg( feature = "compiler" ) ]
797
+ fn with_huffman_tree < Pk : MiniscriptKey > (
798
+ ms : Vec < ( OrdF64 , Miniscript < Pk , Tap > ) > ,
799
+ ) -> Result < TapTree < Pk > , Error > {
800
+ let mut node_weights = BinaryHeap :: < ( Reverse < OrdF64 > , TapTree < Pk > ) > :: new ( ) ;
801
+ for ( prob, script) in ms {
802
+ node_weights. push ( ( Reverse ( prob) , TapTree :: Leaf ( Arc :: new ( script) ) ) ) ;
803
+ }
804
+ if node_weights. is_empty ( ) {
805
+ return Err ( errstr ( "Empty Miniscript compilation" ) ) ;
806
+ }
807
+ while node_weights. len ( ) > 1 {
808
+ let ( p1, s1) = node_weights. pop ( ) . expect ( "len must atleast be two" ) ;
809
+ let ( p2, s2) = node_weights. pop ( ) . expect ( "len must atleast be two" ) ;
810
+
811
+ let p = ( p1. 0 ) . 0 + ( p2. 0 ) . 0 ;
812
+ node_weights. push ( (
813
+ Reverse ( OrdF64 ( p) ) ,
814
+ TapTree :: Tree ( Arc :: from ( s1) , Arc :: from ( s2) ) ,
815
+ ) ) ;
816
+ }
817
+
818
+ debug_assert ! ( node_weights. len( ) == 1 ) ;
819
+ let node = node_weights
820
+ . pop ( )
821
+ . expect ( "huffman tree algorithm is broken" )
822
+ . 1 ;
823
+ Ok ( node)
824
+ }
0 commit comments